Steve Yegge关于服务器端 Javascript的帖子的评论开始讨论语言中类型系统的优点,该评论描述了:
...来自HM风格系统的示例,您可以在其中获得以下内容:
expected signature Int*Int->Int but got Int*Int->Int
你能举一个函数定义的例子(或两个?)和一个会产生该错误的函数调用吗?看起来在大型程序中调试可能非常困难。
另外,我可能在Miranda看到过类似的错误吗?(我已经 15 年没用过了,所以我对它的记忆很模糊)
Steve Yegge关于服务器端 Javascript的帖子的评论开始讨论语言中类型系统的优点,该评论描述了:
...来自HM风格系统的示例,您可以在其中获得以下内容:
expected signature Int*Int->Int but got Int*Int->Int
你能举一个函数定义的例子(或两个?)和一个会产生该错误的函数调用吗?看起来在大型程序中调试可能非常困难。
另外,我可能在Miranda看到过类似的错误吗?(我已经 15 年没用过了,所以我对它的记忆很模糊)
我对 Yegge(和 Ola Bini)关于静态类型的观点持保留态度。如果您欣赏静态类型为您提供的东西,您将了解您选择的编程语言的类型系统是如何工作的。
IIRC,ML 对元组使用 '*' 语法。<type> * <type> 是一个有两个元素的元组类型。因此, (1, 2) 将具有 int * int 类型。
Haskell 和 ML 都使用 -> 作为函数。在 ML 中,int * int -> int 将是一个函数的类型,它接受一个 int 和 int 的元组并将其映射到一个 int。
您可能会看到一个看起来与 Ola 在从另一种语言进入 ML 时引用的错误类似的错误的原因之一是,如果您尝试使用括号和逗号将参数传递给一个函数,就像在 C 或 Pascal 中那样,接受两个参数。
问题是,函数式语言通常将多个参数的函数建模为函数返回函数。所有函数只接受一个参数。如果函数应该接受两个参数,则它会接受一个参数并返回一个具有单个参数的函数,该函数返回最终结果,依此类推。为了使所有这些清晰易读,函数应用只需通过连词即可完成(即将表达式并排放置)。
因此,ML 中的一个简单函数(注意:我使用 F# 作为我的 ML)可能看起来有点像:
let f x y = x + y;;
它有类型:
val f : int -> int -> int
(一个接受整数并返回一个函数的函数,该函数本身接受一个整数并返回一个整数。)
但是,如果你天真地用元组调用它:
f(1, 2)
...你会得到一个错误,因为你将一个 int*int 传递给期望一个 int 的东西。
我认为这是 Ola 试图中伤的“问题”。不过,我认为问题并没有他想的那么严重。当然,在 C++ 模板中情况要糟糕得多。
这可能是指一个编写错误的编译器,它未能插入括号来消除错误消息的歧义。具体来说,该函数需要一个 元组int
并返回一个int
,但您传递了一个元组 ofint
和一个函数 from int
to int
。更具体地说(在 ML 中):
fun f g = g (1, 2);
f (42, fn x => x * 2)
这将产生类似于以下的类型错误:
预期类型
int * int -> int
,得到类型int * (int -> int)
如果省略括号,则此错误可能会令人讨厌地模棱两可。
值得注意的是,这个问题远非 Hindley-Milner 独有。事实上,我想不出任何特定于 HM的奇怪类型错误。至少,没有一个像给出的例子。我怀疑奥拉只是在吹烟。
由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此实际上很容易出现这样的错误,特别是如果您t
在不同模块中为您的类型(例如,)使用了一些通用名称。这是 OCaml 中的一个简单示例:
# 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
类型。稍加努力,我们就可以得到或多或少与上面相同的错误:
# let f g x y = g(x,y) + x + y;;
val f : (int * int -> int) -> int -> int -> int = <fun>
# type int = Foo of int;;
type int = Foo of int
# let h (Foo a, Foo b) = (Foo a);;
val h : int * int -> int = <fun>
# f h;;
This expression has type int * int -> int but is here used with type
int * int -> int