我有类型级算术的实现能够进行一些编译时算术验证,即<,>,=
有两种方式:
有了这些,我可以拥有一个getFoo
函数,我可以像这样调用:
getFoo[_2,_3]
_2
和_3
是整数值2和3的类型等价物。现在理想情况下,我希望我的getFoo
函数将整数值作为参数并尝试推断来自值_2
的{{1}}。
我的计划是将以下的associatedInt信息添加到2
基类:
Nat
以便将后续类型定义为:
trait Nat {
val associatedInt: Int
type AssociatedInt = associatedInt.type
}
然后更改getFoo的签名,使其占用整数:
type _1 = Succ[_0] {
override val associatedInt: Int = 1
}
基于此,我们将使用与def getFoo(i:Int)(implicit ...)
类型相关联的类型执行类型级算术断言。即,像:
AssociatedInt
哪个不行。即:
def getFoo(i: Integer)(implicit ev: Nat{type I = i.type } =:= ExpectedType)
经过反思,我不应该期待它。因为如果我们有:
trait Nat {
val i: Integer
type I = i.type
}
type ExpectedType = _1
trait _1 extends Nat {
override val i: Integer = 1
}
def getFoo(i: Integer)
(implicit ev: Nat{type I = i.type } =:= ExpectedType)= ???
getFoo(1) //this fails to prove the =:= implicit.
即。具有相同值的不同“对象”的单例类型是不同的。 (我想原因是目前在Scala中,单例类型用于对象,与literal type不同)
因此,通过这些背景信息,我想知道是否有任何方法可以实现我想要做的事情,即通过其他方法从关联值推断出类型。
答案 0 :(得分:1)
答案似乎只是“不”。这些值在运行时存在。类型检查在编译时发生。这两个时间间隔不相交,运行时始终严格地在编译时间之后,因此无法及时传播有关值的信息以获取有关类型的其他信息。
请注意,如果您想要一个排序(您不想添加或减去版本号),那么您可以简单地重用子类型关系,如下所示:
sealed trait Num
class _9 extends Num
class _8 extends _9
class _7 extends _8
class _6 extends _7
class _5 extends _6
class _4 extends _5
class _3 extends _4
class _2 extends _3
class _1 extends _2
class _0 extends _1
trait Version[+Major <: Num, +Minor <: Num]
println(implicitly[Version[_2, _4] =:= Version[_2, _4]])
println(implicitly[Version[_2, _3] <:< Version[_2, _4]])
答案 1 :(得分:1)
诀窍是要意识到值可以有类型字段,并且类型信息在编译时可用。考虑到这一点,我们可以定义:
sealed trait NatEnum{
type Nat_ <:Nat
}
并为这些类型定义枚举“值”,如:
object __0 extends NatEnum{ override type Nat_ = _0 }
object __1 extends NatEnum{ override type Nat_ = _1 }
object __2 extends NatEnum{ override type Nat_ = _2 }
object __3 extends NatEnum{ override type Nat_ = _3 }
并重构getFoo
,如下所示:
def getFoo(maj: NatEnum, min: NatEnum)(implicit
maj_check: FooClient.Major =:= maj.Nat_,
min_check: FooClient.Minor <:< min.Nat_
) = FooClient.foo
我们可以测试:
getFoo(__2,__2) //compiles
getFoo(__1,__0)// doesn't compile