理解类型错误:"期望的签名为Int*Int->Int,但得到的是Int*Int->Int"

8

Steve Yegge文章关于服务器端JavaScript的评论开始讨论语言中类型系统的优点,这个评论描述了:

... examples from H-M style systems where you can get things like:

expected signature Int*Int->Int but got Int*Int->Int

你能举一个函数定义(或两个)和一个函数调用的例子,以产生该错误吗?在一个相对较大的程序中,这似乎很难调试。

此外,我是否在Miranda中看到过类似的错误?(我已经15年没有使用它了,所以对它的记忆很模糊)

3个回答

8
我建议你对Yegge(和Ola Bini)关于静态类型的意见持保留态度。如果你欣赏静态类型给你带来的好处,你需要学习所选择编程语言的类型系统。
据我所知,ML使用“*”语法表示元组。<type> * <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的内容,就会出现错误。

我认为这是Ola试图抹黑的“问题”。但我认为这个问题并没有他想象的那么严重,当然,在C++模板中情况要糟糕得多。


1
我会更强烈地表达(加点盐!)……不过,回答很好。 - Konrad Rudolph
谢谢 - 那可能正是我多年前所做的,因为我的第一门语言确实是Pascal和C! - devstopfix
2
那么问题中的引用应该是“预期的签名为Int*Int->Int,但实际得到的是Int->Int->Int”吗?因为我不明白相同类型会是一个错误。 - Jonathan Tran
Jonathan - 我并不是在试图模仿完全相同的错误。我认为 Ola 写代码时可能记忆有些模糊。 - Barry Kelly

4

可能是指编译器编写不良,无法插入括号以消除错误信息的歧义。具体来说,该函数期望一个由int组成的元组,并返回一个int,但你传递了一个由int和从intint的函数组成的元组。更具体地说(在ML中):

fun f g = g (1, 2);

f (42, fn x => x * 2)

这将会产生一个类型错误,类似于以下的错误信息:
期望类型为 int * int -> int,但实际得到的类型是 int * (int -> int) 如果省略了括号,这个错误就会变得非常模糊不清。
值得注意的是,这个问题远非仅限于 Hindley-Milner。事实上,我想不出任何奇怪的类型 错误 是特定于 H-M 的。至少,没有像例子中那样的。我怀疑 Ola 只是在说些空话。

3

由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此如果您在不同的模块中使用某些通用名称(例如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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接