Can you store the type
of an object in Nim, similiar to using the Class
object in Java?
For example, I would like to achieve something like this:
let myType = someObject.type
Assuming that .type
would return the type
of the object. If that is possible, I'd also like to create objects that can store HashSets
of types, ideally using generics to limit the types that can be used:
type System = object
includes: HashSet[type[Component]]
excludes: HashSet[type[Component]]
Or is this something that is currently impossible in Nim?
the typedesc
type is not available at runtime, so it's not possible to store a type as is.
the naive solution would be to just take the string representation of the type:
let myType = $(myObject.type)
but this might not work the way you want if you have type aliases
type
A = seq[int]
here $A != "seq[int]"
even though the two are otherwise identical and interoperable, similarly float64
and float
https://github.com/yglukhov/variant has already implemented these edge cases, so let's leverage that:
nimble install variant
, then, roughly:
import variant
let myTypeId = someObject.getTypeId # the hash of a string representation
myTypeSet.incl myTypeId #put it in your hash set
This concludes the functional portion of this answer, that which extensively follows deals with how to statically error when trying to include an unwanted type.
If you're only interested in limiting inheritable types this is a bit easier than if you want to limit with typeclasses.
import variant,sets
type
TypeSetBase = HashSet[TypeId]
TypeSet*[T] = distinct TypeSetBase
proc initTypeSet*[T](): TypeSet[T] =
TypeSetBase(result).init()
proc incl*[T](ts: var TypeSet[T], x: typedesc[T]) =
TypeSetBase(result).incl getTypeId(x)
proc contains[T](ts: TypeSet[T],x: typedesc): bool =
TypeSetBase(ts).contains getTypeId(x)
type
Foo = object of RootObj
Bar = object of Foo
Baz = object of Foo
Qux = object
var includes = initTypeSet[Foo]()
includes.incl Bar
includes.incl Baz
assert Bar in includes
assert Baz in includes
assert not(Foo in includes)
#includes.incl Qux #static error
For the general case this is harder. Typeclasses won't get us there, as one can't instantiate a TypeSet[int | float]
Here's my solution, using a macro to do the boilerplate for us. this is self-contained.
import macros,variant,sets
type TypeSetBase = HashSet[TypeId]
macro TypeSet*(name,cls,rhs:untyped) =
let tynm = ident("TypeSet_" & cls.repr)
let initnm = ident("init_" & cls.repr)
result = quote do:
when not declared(`tynm`):
type `tynm` = distinct TypeSetBase
proc `initnm`():`tynm` =
TypeSetBase(result).init()
proc incl*(ts: var `tynm`, x:typedesc[`cls`]) =
TypeSetBase(ts).incl getTypeId(x)
proc contains*(ts: `tynm`, x:typedesc):bool =
TypeSetBase(ts).contains getTypeId(x)
var `name` = `initnm`()
import sugar # just nicer for procs
var x{.TypeSet.}:SomeNumber | proc
x.incl float
x.incl (int)->int
x.incl ()->string
#x.incl string
# static error:
# type mismatch: got <TypeSet_SomeNumber | proc, type string>
assert float in x
assert ((int)->int) in x
assert (proc():string) in x
this doesn't get you your System
type yet but i'm out of time for the moment.