Haskell的类型推理引擎比Scala强大得多。在Haskell中,我很少需要显式地编写类型,而在Scala中,类型只能在表达式中推断,而不能在方法定义中推断。
例如,请参阅以下Haskell代码段:
size xs = loop xs 0
where
loop [] acc = acc
loop (_ : xs) acc = loop xs (acc+1)
返回List的大小。 Haskell编译器可以识别使用的类型和函数定义。等效的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)
}
是因为没人实施吗? Scala的类型系统不是像这种情况所需的那么强大吗?还是有其他原因吗?
答案 0 :(得分:54)
主要原因是Scala的类型系统允许子类型,Hindley-Milner type inference algorithm不支持。
Haskell没有子类型,所以算法在那里运行得更好,尽管GHC支持的许多流行类型系统扩展导致类型推断再次失败,迫使你为某些表达式提供显式类型签名。
最后,它是类型系统的力量和可以完成的类型推断量之间的权衡。 Scala和Haskell只是做出了不同的权衡。
答案 1 :(得分:24)
我想已经给出了主要原因,但我发现Scala的创作者Martin Odersky的这句话引起了特别丰富的信息,
Scala没有Hindley / Milner类型推断的原因是 很难与超载等功能结合使用 ad-hoc变体,而不是类型类),记录选择和子类型。 我不是说不可能 - 有许多扩展 结合这些功能;事实上,我一直对他们中的一些人有所了解 我。我只是说要很好地完成这项工作是非常困难的 练习,其中一个人需要有小型表达,而且好 错误消息。这也不是一个关闭案例 - 很多研究人员都是 努力推动这里的界限(例如在雷米看看 MLF)。但是现在它是更好的类型推理对比的权衡 更好地支持这些功能。你可以做出权衡 方法。我们想要与Java集成的事实倾向于扩展 支持亚型和远离欣德利/米尔纳。
来源:帖子Universal Type Inference is a Bad Thing下的评论。
答案 2 :(得分:19)
考虑
def foo(x) = x.a + x.b
Scala怎么可能推断出参数的类型?它应该查找具有a
和b
字段的每个班级吗?如果超过1怎么办?在Haskell
foo x = (a x) + (b x)
记录名称是唯一的,它提出了自己的问题,但意味着您可以随时推断出哪种记录被引用。
case
表达式可以是异构的在Scala中,匹配对象的类型可以用作匹配的一部分,也可以决定应该如何进行匹配。因此,即使case
中的所有构造函数都用于List
,您可能想要将除列表之外的内容传递给它,并使其失败。
答案 3 :(得分:13)
另一个与Hindley-Milner类型推断不相符的是method overloading,以及默认参数和varargs等相关功能。这就是为什么在Haskell中编写像zipWithN这样的东西这么难的原因(在Scala中这很简单)。