Steve Yegge post关于server-side Javascript的评论开始讨论语言类型系统的优点,comment描述了这些:
...来自H-M样式系统的示例,您可以获得以下内容:
expected signature Int*Int->Int but got Int*Int->Int
你能给出一个函数定义(或两个?)和一个会产生错误的函数调用的例子吗?看起来在大型程序中调试可能会非常困难。
另外,我可能在Miranda中看到了类似的错误吗? (我15年没用过了,所以我对它的记忆含糊不清)
答案 0 :(得分:8)
我会把Yegge(和Ola Bini)的观点用于静态打字和一粒盐。如果您了解静态类型为您提供的内容,您将学习如何使用您选择的编程语言的类型系统。
IIRC,ML对元组使用'*'语法。 <类型> *< type>是一个有两个元素的元组类型。所以,(1,2)将具有int * int类型。
Haskell和ML都使用 - >用于功能。在ML中,int * int - > int将是一个函数的类型,它接受int和int的元组并将其映射到int。
你可能会看到一个看起来模糊的错误的原因之一就是你从另一种语言来到ML时引用的那个Ola,如果你尝试使用括号和逗号传递参数,就像在C或Pascal中那样,一个带两个参数的函数。
问题是,函数式语言通常将多个参数的函数建模为函数返回函数;所有函数只接受一个参数。如果函数应该接受两个参数,则它接受一个参数并返回一个参数的函数,该函数返回最终结果,依此类推。为了使所有这些清晰,功能应用程序只需通过连接(即将表达式放在彼此旁边)完成。所以,ML中的一个简单函数(注意:我使用F#作为我的ML)可能看起来有点像:
let f x y = x + y;;
它有类型:
val f : int -> int -> int
(一个取整数并返回一个函数的函数,该函数本身取一个整数并返回一个整数。)
但是,如果你天真地用元组调用它:
f(1, 2)
...你会得到一个错误,因为你把一个int * int传递给了一个期望int的东西。
我希望这是奥拉试图诽谤的“问题”。我不认为这个问题和他想的一样糟糕;当然,它在C ++模板中要糟糕得多。
答案 1 :(得分:4)
这可能是因为编写错误的编译器无法插入括号来消除错误消息的歧义。具体来说,该函数需要int
的元组并返回int
,但您传递了int
元组和int
到int
的函数。更具体地说(在ML中):
fun f g = g (1, 2);
f (42, fn x => x * 2)
这将产生类似于以下类型的错误:
预期类型
int * int -> int
,类型为int * (int -> int)
如果省略括号,则此错误可能会令人讨厌。
值得注意的是,这个问题远非针对Hindley-Milner。事实上,我想不出任何特定于H-M的奇怪类型错误。至少,没有人喜欢给出的例子。我怀疑奥拉只是抽烟。
答案 2 :(得分:3)
由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此实际上很容易出现这样的错误,特别是如果您对类型使用某些通用名称(例如,{{ 1}})在不同的模块中。这是OCaml中的一个简单示例:
t
我在这里所做的是将类型标识符# let f x = x + 1;;
val f : int -> int = <fun>
# type int = Foo of string;;
type int = Foo of string
# f (Foo "hello");;
This expression has type int but is here used with type int
重新绑定到与内置int
类型不兼容的新类型。通过更多努力,我们可以获得与上述相同的错误:
int