总结“大”Nat的

时间:2017-02-23 03:29:33

标签: scala shapeless

假设:

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是否甚至存在于无形状中?

1 个答案:

答案 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,因此第一个显然是明确的。它知道_4Succ[_3]相同(在一秒内更多),因此它会sum2 A作为_3B { {1}} _4

这意味着我们需要找到Sum[_3, _5]个实例。 sum1出于与以前类似的原因,我们再次尝试sum2,这次是A = _2B = _5,这意味着我们需要Sum[_2, _6],通过sum2A = _1让我们回到B = _6,我们会向我们发送Sum[_1, _7]。这是我们最后一次使用sum2A = _0B = _7。这次当我们寻找Sum[_0, _8]时,我们会点击sum1,我们就完成了。

所以很明显,对于n + n我们将不得不进行n + 1隐式搜索,并且在每一个编译器中都将进行类型相等检查和其他内容(更新:请参阅Miles的答案解释了这里最大的问题是什么)需要遍历Nat类型的结构,所以我们处于指数范围。编译器真的,实际上并不是设计用于有效地使用这样的类型,这意味着即使对于小数字,这个操作也需要很长时间。

附注1:在Shapeless

中实现

脱离我的头脑我不完全确定为什么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分钟计算。

附注2:归纳启发式

这似乎不是Typelevel Scala -Yinduction-heuristics帮助的情况 - 我只是尝试使用@inductive Sum上的_1注释来编译Shapeless,它看起来仍然非常可怕没有它就慢。

44呢?

_2_3type _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)); 类别别名。