我正在尝试使用类型级编程来概括Scala中的可分割关系。这是我的Nat
号码定义:
trait Nat {
type plus[N<:Nat] <: Nat
}
trait Zero extends Nat {
type plus[N<:Nat] = N
}
trait Succ[N <: Nat] extends Nat {
type plus[M<:Nat] = Succ[M#plus[N]]
}
对于说3号,这有效:
trait DivBy3[N<:Nat]
object DivByX {
implicit val DivBy3_0: DivBy3[_0] = new DivBy3[_0]{}
implicit def DivBy3_n[N <: Nat](implicit witness: DivBy3[N]): DivBy3[Succ[Succ[Succ[N]]]] =
new DivBy3[Succ[Succ[Succ[N]]]]{}
implicitly[DivBy3[_0]] // <- compiles, good!
//implicitly[DivBy3[_1]] <- doesn't compile, good!
//implicitly[DivBy3[_2]] <- doesn't compile, good!
implicitly[DivBy3[_3]] // <- compiles, good!
//implicitly[DivBy3[_4]] <- doesn't compile, good!
//implicitly[DivBy3[_5]] <- doesn't compile, good!
implicitly[DivBy3[_6]] // <- compiles, good!
}
当我尝试使用具有2个类型参数的特征来推广它时,它不起作用(类似trait DivByX[X <: Nat, N <: Nat]
)。所以我尝试了以下,但也没有用:
trait Divisors[X <: Nat] {
trait DivByX[N <: Nat]
implicit val DivByX_0: DivByX[_0] = new DivByX[_0]{}
implicit def DivByX_NplusX[N <: Nat](implicit witness: DivByX[N]): DivByX[N#plus[X]] =
new DivByX[N#plus[X]]{}
}
object Divisors extends Divisors[_3] {
type DivBy3[N <: Nat] = DivByX[N]
implicitly[DivBy3[_0]] // <- it compiles, meh!
//implicitly[DivBy3[_1]] <- doesn't compile, good!
//implicitly[DivBy3[_2]] <- doesn't compile, good!
//implicitly[DivBy3[_3]] <- doesn't compile, baaad!
}
什么是推广这个的正确方法?
编辑:关注@TravisBrown的建议,但不编译为3 | 6
:
trait Nat
trait Zero extends Nat
trait Succ[N <: Nat] extends Nat
trait Adder[N <: Nat, M <: Nat] {
type Out <: Nat
}
object Adder {
type Aux[N<:Nat,M<:Nat, Out0 <: Nat] = Adder[N,M] { type Out = Out0 }
implicit def add0[N<:Nat]: Aux[N,_0,N] = new Adder[N,_0]{
type Out = N
}
implicit def addN[N<:Nat,M<:Nat](implicit adder: Adder[Succ[N],M]): Aux[N,Succ[M], adder.Out] =
new Adder[N,Succ[M]] {
type Out = adder.Out
}
def apply[N<:Nat, M<: Nat](implicit adder: Adder[N,M]): Aux[N,M, adder.Out] = adder
}
trait DivByX[X <: Nat, N <: Nat]
object DivByX {
implicit def DivByX_0[X <: Nat]: DivByX[X, _0] = new DivByX[X, _0]{}
implicit def DivByX_NplusX[X <: Nat, N <: Nat, S <: Nat](
implicit div: DivByX[X,N], sum: Adder.Aux[N,X,S]): DivByX[X,S] =
new DivByX[X,S]{}
import Adder._
type DivBy3[N <: Nat] = DivByX[_3, N]
implicitly[DivBy3[_0]]
implicitly[DivBy3[_3]]
implicitly[Adder.Aux[_3,_3,_6]]
val x: DivBy3[_6] = DivByX_NplusX(implicitly[DivBy3[_3]], implicitly[Adder.Aux[_3,_3,_6]])
//implicitly[DivBy3[_6]] // <- :( doesn't compile, whyyy?
}
答案 0 :(得分:4)
这将是一个小手淫,因为说实话,我真的不明白这个东西。我不确定那些不太熟悉scalac实现隐式解析的人是否有可能理解这些东西,但至少有可能对编译器喜欢和不喜欢的内容建立一些直觉,那就是我将在这里解释一下。
采取最新的实施:
trait DivByX[X <: Nat, N <: Nat]
object DivByX {
implicit def DivByX_0[X <: Nat]: DivByX[X, _0] = new DivByX[X, _0]{}
implicit def DivByX_NplusX[X <: Nat, N <: Nat, S <: Nat](
implicit div: DivByX[X,N], sum: Adder.Aux[N,X,S]): DivByX[X,S] =
new DivByX[X,S]{}
}
当您要求DivByX[_3, _6]
实例时,编译器将尝试查找提供它的隐式实例。由于DivByX_0
不是_0
,因此_6
会立即停用。接下来,它将尝试DivByX_Nplus
,这意味着它需要按顺序求解X
,N
和S
。 X
和S
部分很容易 - N
这就是问题 - 因为我们知道这个版本不起作用,所以我首先重新安排类型参数来放置未知数最后一个:
trait DivByX[X <: Nat, N <: Nat]
object DivByX {
implicit def DivByX_0[X <: Nat]: DivByX[X, _0] = new DivByX[X, _0]{}
implicit def DivByX_NplusX[X <: Nat, S <: Nat, N <: Nat](
implicit div: DivByX[X,N], sum: Adder.Aux[N,X,S]): DivByX[X,S] =
new DivByX[X,S]{}
}
不幸的是,这仍然不起作用。如果我们启用-Xlog-implicits
,我们会看到以下内容(以及其他一些内容):
scala> implicitly[DivByX[_3, _6]]
...
<console>:16: this.DivByX.DivByX_NplusX is not a valid implicit value for DivByX[_3,_6] because:
hasMatchingSymbol reported error: could not find implicit value for parameter sum: Adder.Aux[N,_3,_6]
implicitly[DivByX[_3, _6]]
^
...
所以看起来问题是它无法解决N
中的N + 3 = 6
。是否有这样的原因?我不知道。我们可以坐下来使用规范和scalac源代码,然后花一点时间试图找出编译器是否有这样做的原因,或者我们可以将我们不知道的移动内容的一般规则应用到最后
我们不能使用我们的Adder
类型类来重写N + 3 = 6
等式,使N
最后出现,但我们可以为新的类型写一个类似的类使这成为可能的操作:
trait Minus[M <: Nat, S <: Nat] { type Out <: Nat }
object Minus {
type Aux[M <: Nat, S <: Nat, Out0 <: Nat] = Minus[M, S] { type Out = Out0 }
def apply[M <: Nat, S <: Nat](implicit m: Minus[M, S]): Aux[M, S, m.Out] = m
implicit def minus0[M <: Nat]: Aux[M, _0, M] = new Minus[M, _0] {
type Out = M
}
implicit def minusN[M <: Nat, S <: Nat](implicit
m: Minus[M, S]
): Aux[Succ[M], Succ[S], m.Out] = new Minus[Succ[M], Succ[S]] {
type Out = m.Out
}
}
我们可以确认它看起来像我们认为的那样:
scala> implicitly[Minus.Aux[_6, _1, _5]]
res6: Minus.Aux[_6,_1,_5] = Minus$$anon$2@2d4203b4
scala> implicitly[Minus.Aux[_6, _3, _3]]
res7: Minus.Aux[_6,_3,_3] = Minus$$anon$2@2a071dd
scala> implicitly[Minus.Aux[_6, _3, _2]]
<console>:16: this.Minus.minusN is not a valid implicit value for Minus.Aux[_6,_3,_2] because:
hasMatchingSymbol reported error: type mismatch;
...
现在我们可以尝试再次撰写DivByX
,将Adder
替换为Minus
:
trait DivByX[X <: Nat, N <: Nat]
object DivByX {
implicit def divByX0[X <: Nat]: DivByX[X, _0] = new DivByX[X, _0] {}
implicit def DivByXS[X <: Nat, S <: Nat, N <: Nat](implicit
div: DivByX[X, N],
minus: Minus.Aux[S, X, N]
): DivByX[X, S] = new DivByX[X, S] {}
}
......但它仍然不起作用:
scala> implicitly[DivByX[_6, _3]]
<console>:16: error: could not find implicit value for parameter e: DivByX[_6,_3]
implicitly[DivByX[_6, _3]]
^
所以我们只是应用我们将“更多未知”的东西向下移动到右边的规则,这次查看divByXS
的隐含参数。在div
类型中,我们同时看到X
(我们知道)和N
(我们没有),而对于minus
,我们看到了两件我们知道的事情( S
和X
)和我们没有的N
。所以我们尝试切换他们的订单,以便我们更了解的是第一个:
trait DivByX[X <: Nat, N <: Nat]
object DivByX {
implicit def divByX0[X <: Nat]: DivByX[X, _0] = new DivByX[X, _0] {}
implicit def DivByXS[X <: Nat, S <: Nat, N <: Nat](implicit
minus: Minus.Aux[S, X, N],
div: DivByX[X, N]
): DivByX[X, S] = new DivByX[X, S] {}
}
然后我们再试一次:
scala> implicitly[DivByX[_3, _6]]
res0: DivByX[_3,_6] = DivByX$$anon$4@8d6e389
这似乎太好了,所以让我们尝试其他一些东西:
scala> implicitly[DivByX[_3, Succ[Succ[Succ[_6]]]]]
res1: DivByX[_3,Succ[Succ[Succ[_6]]]] = DivByX$$anon$4@4c025f43
scala> implicitly[DivByX[_2, _6]]
res2: DivByX[_2,_6] = DivByX$$anon$4@4d4069f9
scala> implicitly[DivByX[_2, _4]]
res3: DivByX[_2,_4] = DivByX$$anon$4@40dc52e
scala> implicitly[DivByX[_1, _5]]
res4: DivByX[_1,_5] = DivByX$$anon$4@5bd63e47
scala> implicitly[DivByX[_3, _5]]
<console>:16: error: could not find implicit value for parameter e: DivByX[_3,_5]
implicitly[DivByX[_3, _5]]
^
是的,看起来它有效,即使我们不知道它为什么有效,至少我们是以模糊的原则来到这里。