The comments on Steve Yegge's post about server-side Javascript started discussing the merits of type systems in languages and this comment describes:
关于Steve Yegge关于服务器端Javascript的帖子的评论开始讨论语言类型系统的优点,这篇评论描述:
... examples from H-M style systems where you can get things like:
...来自H-M风格系统的例子,你可以得到这样的东西:
expected signature Int*Int->Int but got Int*Int->Int
Can you give an example of a function definition (or two?) and a function call that would produce that error? That looks like it might be quite hard to debug in a large-ish program.
你能给出一个函数定义(或两个?)和一个会产生错误的函数调用的例子吗?看起来在大型程序中调试可能非常困难。
Also, might I have seen a similar error in Miranda? (I have not used it in 15 years and so my memory of it is vague)
另外,我可能在米兰达看到过类似的错误吗? (我15年没用过了,所以我对它的记忆含糊不清)
3 个解决方案
#1
8
I'd take Yegge's (and Ola Bini's) opinions on static typing with a grain of salt. If you appreciate what static typing gives you, you'll learn how the type system of the programming language you choose works.
我会把Yegge(和Ola Bini)的观点用于静态打字和一粒盐。如果您了解静态类型为您提供的内容,您将学习如何使用您所选择的编程语言的类型系统。
IIRC, ML uses the '*' syntax for tuples. <type> * <type> is a tuple type with two elements. So, (1, 2) would have int * int type.
IIRC,ML使用元组的'*'语法。
Both Haskell and ML use -> for functions. In ML, int * int -> int would be the type of a function that takes a tuple of int and int and maps it to an int.
Haskell和ML都使用 - >用于函数。在ML中,int * int - > int将是一个函数的类型,它接受int和int的元组并将其映射到int。
One of the reasons you might see an error that looks vaguely like the one Ola quoted when coming to ML from a different language, is if you try and pass arguments using parentheses and commas, like one would in C or Pascal, to a function that takes two parameters.
你可能会看到一个看起来模糊的错误的原因之一就是当你从另一种语言来到ML时引用的那个Ola,如果你试图用括号和逗号(如C或Pascal中的那个)将参数传递给一个函数有两个参数。
The trouble is, functional languages generally model functions of more than one parameter as functions returning functions; all functions only take a single argument. If the function should take two arguments, it instead takes an argument and returns a function of a single argument, which returns the final result, and so on. To make all this legible, function application is done simply by conjunction (i.e. placing the expressions beside one another).
问题是,函数式语言通常将多个参数的函数建模为函数返回函数;所有函数只接受一个参数。如果函数应该接受两个参数,则它接受一个参数并返回一个参数的函数,该函数返回最终结果,依此类推。为了使所有这些易读,功能应用程序只需通过连接(即将表达式放在彼此旁边)完成。
So, a simple function in ML (note: I'm using F# as my ML) might look a bit like:
所以,ML中的一个简单函数(注意:我使用F#作为我的ML)可能看起来有点像:
let f x y = x + y;;
It has type:
它有类型:
val f : int -> int -> int
(A function taking an integer and returning a function which itself takes an integer and returns an integer.)
(一个取整数并返回一个函数的函数,该函数本身取一个整数并返回一个整数。)
However, if you naively call it with a tuple:
但是,如果你天真地用元组调用它:
f(1, 2)
... you'll get an error, because you passed an int*int to something expecting an int.
...你会得到一个错误,因为你把一个int * int传递给了一个期望int的东西。
I expect that this is the "problem" Ola was trying to cast aspersions at. I don't think the problem is as bad as he thinks, though; certainly, it's far worse in C++ templates.
我希望这是奥拉试图诽谤的“问题”。不过,我认为问题并不像他想的那么糟糕;当然,它在C ++模板中要糟糕得多。
#2
4
It's possible that this was in reference to a badly-written compiler which failed to insert parentheses to disambiguate error messages. Specifically, the function expected a tuple of int
and returned an int
, but you passed a tuple of int
and a function from int
to int
. More concretely (in ML):
这可能是因为编写错误的编译器无法插入括号来消除错误消息的歧义。具体来说,函数期望int的元组并返回一个int,但是你传递了一个int元组和一个从int到int的函数。更具体地说(在ML中):
fun f g = g (1, 2);
f (42, fn x => x * 2)
This will produce a type error similar to the following:
这将产生类似于以下类型的错误:
Expected type
int * int -> int
, got typeint * (int -> int)
预期类型int * int - > int,得到类型int *(int - > int)
If the parentheses are omitted, this error can be annoyingly ambiguous.
如果省略括号,则此错误可能会令人烦恼。
It's worth noting that this problem is far from being specific to Hindley-Milner. In fact, I can't think of any weird type errors which are specific to H-M. At least, none like the example given. I suspect that Ola was just blowing smoke.
值得注意的是,这个问题远非针对Hindley-Milner。事实上,我想不出任何特定于H-M的怪异类型错误。至少,没有人喜欢给出的例子。我怀疑奥拉只是吹烟。
#3
3
Since many functional language allow you to rebind type names in the same way you can rebind variables, it's actually quite easy to end up with an error like this, especially if you use somewhat generic names for your types (e.g., t
) in different modules. Here's a simple example in OCaml:
由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此实际上很容易出现这样的错误,特别是如果您在不同模块中对类型使用某些通用名称(例如,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
What I've done here is rebind the type identifier int
to a new type that is incompatible with the built-in int
type. With a little bit more effort, we can get more-or-less the same error as above:
我在这里所做的是将类型标识符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
#1
8
I'd take Yegge's (and Ola Bini's) opinions on static typing with a grain of salt. If you appreciate what static typing gives you, you'll learn how the type system of the programming language you choose works.
我会把Yegge(和Ola Bini)的观点用于静态打字和一粒盐。如果您了解静态类型为您提供的内容,您将学习如何使用您所选择的编程语言的类型系统。
IIRC, ML uses the '*' syntax for tuples. <type> * <type> is a tuple type with two elements. So, (1, 2) would have int * int type.
IIRC,ML使用元组的'*'语法。
Both Haskell and ML use -> for functions. In ML, int * int -> int would be the type of a function that takes a tuple of int and int and maps it to an int.
Haskell和ML都使用 - >用于函数。在ML中,int * int - > int将是一个函数的类型,它接受int和int的元组并将其映射到int。
One of the reasons you might see an error that looks vaguely like the one Ola quoted when coming to ML from a different language, is if you try and pass arguments using parentheses and commas, like one would in C or Pascal, to a function that takes two parameters.
你可能会看到一个看起来模糊的错误的原因之一就是当你从另一种语言来到ML时引用的那个Ola,如果你试图用括号和逗号(如C或Pascal中的那个)将参数传递给一个函数有两个参数。
The trouble is, functional languages generally model functions of more than one parameter as functions returning functions; all functions only take a single argument. If the function should take two arguments, it instead takes an argument and returns a function of a single argument, which returns the final result, and so on. To make all this legible, function application is done simply by conjunction (i.e. placing the expressions beside one another).
问题是,函数式语言通常将多个参数的函数建模为函数返回函数;所有函数只接受一个参数。如果函数应该接受两个参数,则它接受一个参数并返回一个参数的函数,该函数返回最终结果,依此类推。为了使所有这些易读,功能应用程序只需通过连接(即将表达式放在彼此旁边)完成。
So, a simple function in ML (note: I'm using F# as my ML) might look a bit like:
所以,ML中的一个简单函数(注意:我使用F#作为我的ML)可能看起来有点像:
let f x y = x + y;;
It has type:
它有类型:
val f : int -> int -> int
(A function taking an integer and returning a function which itself takes an integer and returns an integer.)
(一个取整数并返回一个函数的函数,该函数本身取一个整数并返回一个整数。)
However, if you naively call it with a tuple:
但是,如果你天真地用元组调用它:
f(1, 2)
... you'll get an error, because you passed an int*int to something expecting an int.
...你会得到一个错误,因为你把一个int * int传递给了一个期望int的东西。
I expect that this is the "problem" Ola was trying to cast aspersions at. I don't think the problem is as bad as he thinks, though; certainly, it's far worse in C++ templates.
我希望这是奥拉试图诽谤的“问题”。不过,我认为问题并不像他想的那么糟糕;当然,它在C ++模板中要糟糕得多。
#2
4
It's possible that this was in reference to a badly-written compiler which failed to insert parentheses to disambiguate error messages. Specifically, the function expected a tuple of int
and returned an int
, but you passed a tuple of int
and a function from int
to int
. More concretely (in ML):
这可能是因为编写错误的编译器无法插入括号来消除错误消息的歧义。具体来说,函数期望int的元组并返回一个int,但是你传递了一个int元组和一个从int到int的函数。更具体地说(在ML中):
fun f g = g (1, 2);
f (42, fn x => x * 2)
This will produce a type error similar to the following:
这将产生类似于以下类型的错误:
Expected type
int * int -> int
, got typeint * (int -> int)
预期类型int * int - > int,得到类型int *(int - > int)
If the parentheses are omitted, this error can be annoyingly ambiguous.
如果省略括号,则此错误可能会令人烦恼。
It's worth noting that this problem is far from being specific to Hindley-Milner. In fact, I can't think of any weird type errors which are specific to H-M. At least, none like the example given. I suspect that Ola was just blowing smoke.
值得注意的是,这个问题远非针对Hindley-Milner。事实上,我想不出任何特定于H-M的怪异类型错误。至少,没有人喜欢给出的例子。我怀疑奥拉只是吹烟。
#3
3
Since many functional language allow you to rebind type names in the same way you can rebind variables, it's actually quite easy to end up with an error like this, especially if you use somewhat generic names for your types (e.g., t
) in different modules. Here's a simple example in OCaml:
由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此实际上很容易出现这样的错误,特别是如果您在不同模块中对类型使用某些通用名称(例如,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
What I've done here is rebind the type identifier int
to a new type that is incompatible with the built-in int
type. With a little bit more effort, we can get more-or-less the same error as above:
我在这里所做的是将类型标识符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