如何在 Nim 中存储对类型的引用?
How to store references to types in Nim?
你能在 Nim 中存储一个对象的 type
,类似于在 Java 中使用 Class
对象吗?
例如,我想实现这样的目标:
let myType = someObject.type
假设 .type
将 return 对象的 type
。如果可能的话,我还想创建可以存储 HashSets
类型的对象,理想情况下使用泛型来限制可以使用的类型:
type System = object
includes: HashSet[type[Component]]
excludes: HashSet[type[Component]]
或者这在 Nim 目前是不可能的?
如果你想限制泛型接受的类型,你可以使用类型class。
https://nim-lang.org/docs/manual.html#generics-type-classes
例如:
# Generic type only takes string or int.
type
Foo[T: string or int] = object
x: T
var
a: Foo[string]
b: Foo[int]
#var c: Foo[float] # Compile error
# Generic proc takes any type of arguments excepts float and tuple.
proc bar[T: not (float or tuple)](x: T) = discard
bar(1)
bar("a")
#bar(1.1) # Compile error
#bar((1, 1)) # Compile error
Nim 不像 java 那样支持反射。您只能在编译时使用类型进行操作。不过,您可以做的是在编译时将所有需要的类型转换为 id,然后改用 id。现在我们如何生成ID?实际上,nim 有一个使用全局宏数据的简单而巧妙的解决方案。
import macrocache
type TypeID* = uint16
const nextTypeID = CacheCounter("nextTypeID")
converter typeID*(T:typedesc): TypeID =
const id = nextTypeID.value
static:
inc nextTypeID
return id.TypeID
when isMainModule:
assert float == 0
assert float == 0
assert int == 1
assert float64 == 0
assert string == 2
assert (float, int) == 3
每次为类型编译 typeID 时,它都会将缓存的计数器状态存储在编译主体中的常量中。每次函数编译时,静态块都会 运行,因此每次传递不同的类型时,计数器都会增加。
存储类型
typedesc
类型在运行时不可用,因此无法按原样存储类型。
天真的解决方案是只采用以下类型的字符串表示形式:
let myType = $(myObject.type)
但如果您有类型别名,这可能无法按照您想要的方式工作
type
A = seq[int]
此处 $A != "seq[int]"
尽管两者在其他方面相同且可互操作,类似地 float64
和 float
https://github.com/yglukhov/variant 已经实现了这些边缘案例,所以让我们利用它:
nimble install variant
,那么,大致是:
import variant
let myTypeId = someObject.getTypeId # the hash of a string representation
myTypeSet.incl myTypeId #put it in your hash set
此答案的功能部分到此结束,后面广泛涉及如何在尝试包含不需要的类型时出现静态错误。
限制可以包含的类型
如果您只对限制可继承类型感兴趣,这比您想要限制类型类要容易一些。
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
对于一般情况,这更难。类型类不会让我们到达那里,因为不能实例化 TypeSet[int | float]
这是我的解决方案,使用宏为我们制作样板文件。这是独立的。
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
这还没有让你找到你的 System
类型,但我现在没时间了。
你能在 Nim 中存储一个对象的 type
,类似于在 Java 中使用 Class
对象吗?
例如,我想实现这样的目标:
let myType = someObject.type
假设 .type
将 return 对象的 type
。如果可能的话,我还想创建可以存储 HashSets
类型的对象,理想情况下使用泛型来限制可以使用的类型:
type System = object
includes: HashSet[type[Component]]
excludes: HashSet[type[Component]]
或者这在 Nim 目前是不可能的?
如果你想限制泛型接受的类型,你可以使用类型class。 https://nim-lang.org/docs/manual.html#generics-type-classes
例如:
# Generic type only takes string or int.
type
Foo[T: string or int] = object
x: T
var
a: Foo[string]
b: Foo[int]
#var c: Foo[float] # Compile error
# Generic proc takes any type of arguments excepts float and tuple.
proc bar[T: not (float or tuple)](x: T) = discard
bar(1)
bar("a")
#bar(1.1) # Compile error
#bar((1, 1)) # Compile error
Nim 不像 java 那样支持反射。您只能在编译时使用类型进行操作。不过,您可以做的是在编译时将所有需要的类型转换为 id,然后改用 id。现在我们如何生成ID?实际上,nim 有一个使用全局宏数据的简单而巧妙的解决方案。
import macrocache
type TypeID* = uint16
const nextTypeID = CacheCounter("nextTypeID")
converter typeID*(T:typedesc): TypeID =
const id = nextTypeID.value
static:
inc nextTypeID
return id.TypeID
when isMainModule:
assert float == 0
assert float == 0
assert int == 1
assert float64 == 0
assert string == 2
assert (float, int) == 3
每次为类型编译 typeID 时,它都会将缓存的计数器状态存储在编译主体中的常量中。每次函数编译时,静态块都会 运行,因此每次传递不同的类型时,计数器都会增加。
存储类型
typedesc
类型在运行时不可用,因此无法按原样存储类型。
天真的解决方案是只采用以下类型的字符串表示形式:
let myType = $(myObject.type)
但如果您有类型别名,这可能无法按照您想要的方式工作
type
A = seq[int]
此处 $A != "seq[int]"
尽管两者在其他方面相同且可互操作,类似地 float64
和 float
https://github.com/yglukhov/variant 已经实现了这些边缘案例,所以让我们利用它:
nimble install variant
,那么,大致是:
import variant
let myTypeId = someObject.getTypeId # the hash of a string representation
myTypeSet.incl myTypeId #put it in your hash set
此答案的功能部分到此结束,后面广泛涉及如何在尝试包含不需要的类型时出现静态错误。
限制可以包含的类型
如果您只对限制可继承类型感兴趣,这比您想要限制类型类要容易一些。
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
对于一般情况,这更难。类型类不会让我们到达那里,因为不能实例化 TypeSet[int | float]
这是我的解决方案,使用宏为我们制作样板文件。这是独立的。
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
这还没有让你找到你的 System
类型,但我现在没时间了。