在类型和值之间建立联系

时间:2018-06-08 14:20:42

标签: scala types type-deduction type-level-computation type-variables

我有类型级算术的实现能够进行一些编译时算术验证,即<,>,=有两种方式:

有了这些,我可以拥有一个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不同)

因此,通过这些背景信息,我想知道是否有任何方法可以实现我想要做的事情,即通过其他方法从关联值推断出类型。

2 个答案:

答案 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

这是要点的更新版本:simplerigorous