假设:
scala> import shapeless.nat.
_0 _10 _12 _14 _16 _18 _2 _21 _3 _5 _7 _9 natOps
_1 _11 _13 _15 _17 _19 _20 _22 _4 _6 _8 apply toInt
scala> import shapeless.ops.nat._
import shapeless.ops.nat._
之后> 3分钟,以下代码没有编译/运行。为什么?
scala> Sum[_22, _22]
另外,看看上面的REPL自动完成,_44
是否甚至存在于无形状中?
答案 0 :(得分:7)
让我们从一个较小的数字开始。当你要求Sum[_4, _4]
时,编译器会去寻找一个实例,它会找到these two methods:
implicit def sum1[B <: Nat]: Aux[_0, B, B] = new Sum[_0, B] { type Out = B }
implicit def sum2[A <: Nat, B <: Nat](implicit
sum: Sum[A, Succ[B]]
): Aux[Succ[A], B, sum.Out] = new Sum[Succ[A], B] { type Out = sum.Out }
由于_4
不是_0
,因此第一个显然是明确的。它知道_4
与Succ[_3]
相同(在一秒内更多),因此它会sum2
A
作为_3
和B
{ {1}} _4
。
这意味着我们需要找到Sum[_3, _5]
个实例。 sum1
出于与以前类似的原因,我们再次尝试sum2
,这次是A = _2
和B = _5
,这意味着我们需要Sum[_2, _6]
,通过sum2
和A = _1
让我们回到B = _6
,我们会向我们发送Sum[_1, _7]
。这是我们最后一次使用sum2
,A = _0
和B = _7
。这次当我们寻找Sum[_0, _8]
时,我们会点击sum1
,我们就完成了。
所以很明显,对于n + n
我们将不得不进行n + 1
隐式搜索,并且在每一个编译器中都将进行类型相等检查和其他内容(更新:请参阅Miles的答案解释了这里最大的问题是什么)需要遍历Nat
类型的结构,所以我们处于指数范围。编译器真的,实际上并不是设计用于有效地使用这样的类型,这意味着即使对于小数字,这个操作也需要很长时间。
脱离我的头脑我不完全确定为什么sum2
没有像这样定义:
implicit def sum2[A <: Nat, B <: Nat](implicit
sum: Sum[A, B]
): Aux[Succ[A], B, Succ[sum.Out]] = new Sum[Succ[A], B] { type Out = Succ[sum.Out] }
这个速度要快得多,至少在我的机器上,Sum[_18, _18]
在4秒钟内编译而不是7分钟计算。
这似乎不是Typelevel Scala -Yinduction-heuristics
帮助的情况 - 我只是尝试使用@inductive
Sum
上的_1
注释来编译Shapeless,它看起来仍然非常可怕没有它就慢。
_2
,_3
,type _23 = Succ[_22]
类型别名在Shapeless中由this boilerplate generator生成的代码中定义,该代码仅配置为生成最多22的值。具体而言,这是一个完全随意的限制。我们可以编写以下内容,例如:
_N
我们完成了与代码生成器完全相同的功能,但又向前迈进了一步。
尽管如此,Shapeless的Nat
别名在22处停止并不重要,因为它们只是别名。关于_N
的重要一点是它的结构,这与我们可能拥有的任何好名字无关。即使Shapeless根本没有提供任何import shapeless.Succ, shapeless.nat._0, shapeless.ops.nat.Sum
Sum[Succ[Succ[_0]], Succ[Succ[_0]]]
别名,我们仍然可以编写如下代码:
Sum[_2, _2]
它与编写Sum[_22, _22]
完全相同,只是输入更加烦人。
因此,当您编写Succ
时,编译器在表示结果类型(即_0
周围的44 _44
)时不会遇到任何问题,即使它没有有一个UPDATE tablename SET columnname1=columnname1-1 WHERE (columnname1,columnname2) in ((23,50),(23,50));
类别别名。