I'm playing with unsafe stuff, and getting unsafe results, but I can't wrap my head around why the error exists from the second transformation
import std/[typetraits]
type
State = enum
sGas
sLiquid
sSolid
Experiment[T; MaterialState: static State] = object
data: T
proc `=copy`[T, Y](dest: var Experiment[T, Y], source: Experiment[T, Y]) {.error: "do not copy".}
proc `=dup`[T, Y](x: Experiment[T, Y]): Experiment[T, Y] {.error: "do not duplicate".}
func state[T, Y](e: Experiment[T, Y]): State =
e.typeof.genericParams.get(1).value
proc initExperiment[T; MaterialState: static State](initialData: T): Experiment[T, MaterialState] =
Experiment[T, MaterialState](data: initialData)
proc `$`[T, Y](e: Experiment[T, Y]): string =
$e.state & " " & $e.data
func condense[T](e: Experiment[T, sGas]): var Experiment[T, sLiquid] =
cast[ptr Experiment[T, sLiquid]](e.addr)[]
func deposit[T](e: var Experiment[T, sGas]): var Experiment[T, sSolid] =
cast[ptr Experiment[T, sSolid]](e.addr)[]
func evaporate[T](e: var Experiment[T, sLiquid]): var Experiment[T, sGas] =
cast[ptr Experiment[T, sGas]](e.addr)[]
func solidify[T](e: var Experiment[T, sLiquid]): var Experiment[T, sSolid] =
cast[ptr Experiment[T, sSolid]](e.addr)[]
func melt[T](e: var Experiment[T, sSolid]): var Experiment[T, sLiquid] =
cast[ptr Experiment[T, sLiquid]](e.addr)[]
func sublimate[T](e: var Experiment[T, sSolid]): var Experiment[T, sGas] =
cast[ptr Experiment[T, sGas]](e.addr)[]
proc main =
echo initExperiment[int, sGas](initialData = 42)
echo initExperiment[int, sGas](initialData = 42).condense()
echo initExperiment[int, sGas](initialData = 42).condense().solidify()
echo initExperiment[int, sGas](initialData = 42).condense().solidify().sublimate()
echo initExperiment[int, sGas](initialData = 42).condense().solidify().sublimate().deposit()
if isMainModule:
main()
output:
sGas 42
sLiquid 42
sSolid 140727473739576
sGas 140727473739576
sSolid 140727473739576
nevermind, just spotted the missing var parameter in
func condense[T](e: Experiment[T, sGas]): var Experiment[T, sLiquid] =
cast[ptr Experiment[T, sLiquid]](e.addr)[]
but I can't find a way to avoid casts or copies while transforming and an Experiment object
func condense[T](e: Experiment[T, sGas]): var Experiment[T, sLiquid] =
cast[ptr Experiment[T, sLiquid]](e.addr)[]
Shouldn't the e is var Experiment[T, sGas] ? Is that typo or intended?
If it's intended, based on the usage, this shouldn't compile though because the e is not addressable.
it was not intendend, and it works correctly after fixing it
but fixing that was just one step in the middle to the final target of this experiment: change object states without performing copies, but in a safe way. I'm trying to remove all the var and casts and favor of sink and lent, without success so far
what I would like to do is:
var e1 = initExperiment[int, sGas](initialData = 42)
var e2 = e1.condense()
var e3 = e2.solidify()
var e4 = e3.sublimate()
var e5 = e4.deposit()
but I keep facing errors like
Error: '=copy' is not available for type <Experiment>; requires a copy because it's not the last read of 'condense(e1)'; routine: main
Thanks for the answer. I am aware that using ptr does work, but the exercise was about trying to use this type pattern without relying on unsafe features, if possible.
Objectives:
So I was trying with hidden pointers like sink/lent/var, without success (so far)
type
State = enum
sGas
sLiquid
sSolid
Experiment[T; MaterialState: static State] = object
data: T
proc `=copy`[T, Y](dest: var Experiment[T, Y], source: Experiment[T, Y]) {.error: "do not copy".}
proc `=dup`[T, Y](x: Experiment[T, Y]): Experiment[T, Y] {.error: "do not duplicate".}
func initExperiment[T; MaterialState: static State](initialData: T): Experiment[T, MaterialState] =
Experiment[T, MaterialState](data: initialData)
func `$`[T; MaterialState: static State](e: Experiment[T, MaterialState] | ptr Experiment[T, MaterialState]): string =
$e.MaterialState & " " & $e.data
func condense[T](e: ptr Experiment[T, sGas]): ptr Experiment[T, sLiquid] =
cast[result.typeof](e)
func deposit[T](e: ptr Experiment[T, sGas]): ptr Experiment[T, sSolid] =
cast[result.typeof](e)
func evaporate[T](e: ptr Experiment[T, sLiquid]): ptr Experiment[T, sGas] =
cast[result.typeof](e)
func solidify[T](e: ptr Experiment[T, sLiquid]): ptr Experiment[T, sSolid] =
cast[result.typeof](e)
func melt[T](e: ptr Experiment[T, sSolid]): ptr Experiment[T, sLiquid] =
cast[result.typeof](e)
func sublimate[T](e: ptr Experiment[T, sSolid]): ptr Experiment[T, sGas] =
cast[result.typeof](e)
proc main =
var e = initExperiment[int, sGas](initialData = 42)
var e1 = e.addr
echo e1
let e2 = e1.condense()
echo e2
let e3 = e2.solidify()
echo e3
let e4 = e3.sublimate()
echo e4
let e5 = e4.deposit()
echo e5
if isMainModule:
main()
output:
sGas 42
sLiquid 42
sSolid 42
sGas 42
sSolid 42
It's likely the best way to achieve what you're after is to use distinct typing, so you can convert inbetween them:
type
State = enum
sGas
sLiquid
sSolid
ExperimentImpl[T] = object
data: T
Experiment[T; MaterialState: static State] = distinct ExperimentImpl[T]
proc `=copy`[T](dest: var ExperimentImpl[T], source: ExperimentImpl[T]) {.error: "do not copy".}
proc `=dup`[T](x: ExperimentImpl[T]): ExperimentImpl[T] {.error: "do not duplicate".}
func initExperiment[T; MaterialState: static State](initialData: T): Experiment[T, MaterialState] =
Experiment[T, MaterialState](ExperimentImpl[T](data: initialData))
func `$`[T; MaterialState: static State](e: Experiment[T, MaterialState]): string =
$MaterialState & " " & $ExperimentImpl[T](e).data
func condense[T](e: sink Experiment[T, sGas]): Experiment[T, sLiquid] =
Experiment[T, sLiquid](e)
func deposit[T](e: sink Experiment[T, sGas]): Experiment[T, sSolid] =
Experiment[T, sSolid](e)
func evaporate[T](e: sink Experiment[T, sLiquid]): Experiment[T, sGas] =
Experiment[T, sGas](e)
func solidify[T](e: sink Experiment[T, sLiquid]): Experiment[T, sSolid] =
Experiment[T, sSolid](e)
func melt[T](e: sink Experiment[T, sSolid]): Experiment[T, sLiquid] =
Experiment[T, sLiquid](e)
func sublimate[T](e: sink Experiment[T, sSolid]): Experiment[T, sGas] =
Experiment[T, sGas](e)
proc main =
var e = initExperiment[int, sGas](initialData = 42)
echo e
let e2 = e.condense()
echo e2
let e3 = e2.solidify()
echo e3
let e4 = e3.sublimate()
echo e4
let e5 = e4.deposit()
echo e5
if isMainModule:
main()
Thanks!!! This is exactly what I was trying to do!
I didn't know that hiding away static generic arguments with type wrapper would enable seamless type conversion.
You always have more than one trick up your sleeve @ElegantBeef