为什么斯卡拉的types推理没有Haskell那么强大?
Haskell的types推断引擎比Scala更强大。 在Haskell中,我很less需要明确地写出types,而在Scala中,types只能在expression式中推断,而在方法定义中则不能。
例如,请参阅以下Haskell代码片段:
size xs = loop xs 0 where loop [] acc = acc loop (_ : xs) acc = loop xs (acc+1)
它返回一个List的大小。 Haskell编译器可以识别使用什么types以及函数定义是什么。 等效的Scala代码:
def size[A]: List[A] => Int = xs => { def loop: (List[A], Int) => Int = { case (Nil, acc) => acc case (_ :: xs, acc) => loop(xs, acc+1) } loop(xs, 0) }
或者用方法定义:
def size[A](xs: List[A]) = { def loop(xs: List[A], acc: Int): Int = xs match { case Nil => acc case _ :: xs => loop(xs, acc+1) } loop(xs, 0) }
我的问题是:为什么我不能像下面这样写他们?
def size = xs => { def loop = { case (Nil, acc) => acc case (_ :: xs, acc) => loop(xs, acc+1) } loop(xs, 0) }
再次用方法定义:
def size(xs) = { def loop(xs, acc) = xs match { case Nil => acc case _ :: xs => loop(xs, acc+1) } loop(xs, 0) }
是因为没有人执行它吗? 斯卡拉的types系统不像这种情况下所需要的那么强大? 或者还有其他原因?
主要原因是Scala的types系统允许使用Hindley-Milnertypes推理algorithm不支持的子typesinput。
Haskell没有子typesinput,所以algorithm在那里效果更好,尽pipeGHC支持的许多受欢迎的types系统扩展导致types推断再次失败,迫使你为某些expression式提供显式的types签名。
最后,这是types系统的权力与可以完成的types推断的量之间的权衡。 斯卡拉和哈斯克尔只是做了不同的权衡。
哈马尔给出了最大的原因。 以下是另外两个:
- Scala使用types来parsing名称
考虑
def foo(x) = xa + xb
斯卡拉怎么可能推断出论证的types? 是否应该寻找每个有a
和b
场的课程? 如果有一个以上呢? 在Haskell
foo x = (ax) + (bx)
logging名称是独一无二的,这表明了它自己的问题,但意味着你总是可以推断出什么样的logging被引用。
- 例如:Scala中的
case
expression式可以是异构的
在Scala中,被匹配的对象的types可以用作匹配的一部分,或者决定如何匹配。 因此,即使所有的构造函数都是List
,你可能想要传递一个列表以外的东西,并让它失败。
我想主要的原因已经被给出了,但是我发现斯卡拉的创造者马丁·奥德斯基(Martin Odersky)的这句话特别提供了信息,
Scala之所以没有Hindley / Milnertypes推理,是因为它与诸如重载(ad-hoc变体,而不是types类),loggingselect和子types等特征相结合是非常困难的。 我不是说不可能的 – 存在一些扩展,纳入这些function; 事实上,我自己也对他们中的一些人有所顾忌。 我只是说在实践中很难做到这一点,在这种情况下需要有小型的expression式和好的错误信息。 这也不是一个closures的情况 – 许多研究人员正在努力推动这里的边界(例如在雷米的MLF)。 但现在这是一个更好的types推理与更好的支持这些function的权衡。 您可以通过两种方式进行权衡。 我们想要与Java集成的事实,倾向于从分类和Hindley / Milner。
资料来源:评论后通用types推断是一件坏事 。
另一个与Hindley-Milnertypes推断不兼容的方法是方法重载 ,以及相关的function,如默认参数和可变参数。 这就是为什么在Haskell中编写像zipWithN这样的东西非常困难的原因(这在Scala中是微不足道的)。