Types 如何在Nim中存储对类型的引用?

Types 如何在Nim中存储对类型的引用?,types,nim-lang,Types,Nim Lang,您能否在Nim中存储对象的类型,类似于在Java中使用类对象? 例如,我希望实现以下目标: let myType = someObject.type 假设.type将返回对象的类型。如果可能的话,我还希望创建可以存储hashset类型的对象,最好使用泛型来限制可以使用的类型: type System = object includes: HashSet[type[Component]] excludes: HashSet[type[Component]] 或者这在Nim中是目

您能否在Nim中存储对象的
类型
,类似于在Java中使用
对象?
例如,我希望实现以下目标:

let myType = someObject.type
假设
.type
将返回对象的
类型。如果可能的话,我还希望创建可以存储
hashset
类型的对象,最好使用泛型来限制可以使用的类型:

type System = object
    includes: HashSet[type[Component]]
    excludes: HashSet[type[Component]]

或者这在Nim中是目前不可能的吗?

如果您想限制泛型采用的类型,可以使用类型类。

例如:

# 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

已经实施了这些边缘案例,因此让我们利用这些优势:

灵活安装变体
,然后大致:

import variant
let myTypeId = someObject.getTypeId # the hash of a string representation
myTypeSet.incl myTypeId #put it in your hash set
这就结束了这个答案的功能部分,下面将详细介绍如何在尝试包含不需要的类型时静态地出错



限制可能包括的类型 如果您只对限制可继承类型感兴趣,那么这比使用TypeClass进行限制要容易一些

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

对于一般情况,这更难。Typeclasses无法实现这一点,因为人们无法实例化
字体[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#更适合procs
var x{.TypeSet.}:SomeNumber | proc
x、 包括浮动
x、 包括(整数)->整数
x、 incl()->string
#x、 包括字符串
#静态错误:
#类型不匹配:得到
断言x中的浮点
在x中断言((int)->int)
在x中断言(proc():字符串)

这还不能得到你的
系统
类型,但我现在没有时间了。

一旦你存储了这些类型,你想用它们做什么?@JakubDóka我想检查序列是否包含这些类型。例如,给定上面的
System
对象和一个
seq[Component]
,我想将序列映射到一个
HashSet[type[Component]
,然后检查它是否包含
系统的所有
包含
和所有
排除
。您可以轻松地将类型转换为唯一的ID,这会有帮助吗?@JakubDóka已经考虑过使用类似的东西,如果它存在的话。你能给我指一下正确的方向吗?我只找到了typetraits模块,它没有提供这种功能。无论如何,这是否意味着无法处理类型本身?您可以处理类型,但只能在编译时处理。有一种简洁明了的解决方案,将ID指定给类型。如果你愿意的话,我可以用它来推一个答案。谢谢,但我的问题不是关于泛型的。我提到它们的唯一原因是如果
类型
类型支持它们(如果存在的话),那就太好了。我建议您删除这个答案,以免混淆其他用户。如果我的答案不对,我也会删除它。通常情况下,一个未被接受的答案对以后的访问者非常有帮助,请不要删除好的答案!很遗憾,不可能直接使用类型,但您提出的解决方案似乎是一个非常好的解决方案。谢谢:)不客气。一般来说,我不希望拖拽所有类型信息的开销,所以我很感激Nim在默认情况下不会这么做。但我承认,当你确实需要它/能够负担得起开销时,它应该更简单。我还意识到,在所有类型都在序列中之后,我还没有解决你这样做的需要(发帖问题)!这是另一回事,我仍然认为您应该使用变体。这也是解决我问题的有效方法,尽管它有点不可预测,因为这些ID可能会更改
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