【问题标题】:Understanding the type error: "expected signature Int*Int->Int but got Int*Int->Int"理解类型错误:“预期签名 Int*Int->Int 但得到 Int*Int->Int”
【发布时间】:2010-09-24 09:18:49
【问题描述】:

Steve Yeggepost 上关于 server-side Javascript 的 cmets 开始讨论语言中类型系统的优点,这个 comment 描述:

...来自H-M 风格系统的示例,您可以在其中获得以下内容:

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

你能举一个函数定义的例子(或两个?)和一个会产生该错误的函数调用吗?看起来在大型程序中调试可能相当困难。

另外,我可能在Miranda 中看到过类似的错误吗? (我已经 15 年没用过了,所以我对它的记忆很模糊)

【问题讨论】:

    标签: haskell ml type-theory


    【解决方案1】:

    我对 Yegge(和 Ola Bini)关于静态类型的观点持保留态度。如果您欣赏静态类型给您带来的好处,您将了解您选择的编程语言的类型系统是如何工作的。

    IIRC,ML 对元组使用 '*' 语法。 * 是一个有两个元素的元组类型。因此,(1, 2) 将具有 int * int 类型。

    Haskell 和 ML 都使用 -> 作为函数。在 ML 中,int * int -> int 是一个函数的类型,它接受一个 int 和 int 的元组并将其映射到一个 int。

    当您从另一种语言进入机器学习时,您可能会看到一个看起来与 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++ 模板中情况要糟糕得多。

    【讨论】:

    • 我会制定更强大的(盐瓶!)......不过,很好的答案。
    • 谢谢 - 这可能正是我多年前所做的事情,因为我的第一门语言确实是 Pascal 和 C!
    • 那么问题中的引用应该是“预期签名 Int*Int->Int 但得到 Int->Int->Int”吗?因为我看不出相同的类型是多么的错误。
    • Jonathan - 我并没有试图模仿完全相同的错误。我希望 Ola 在写这篇文章时记忆力很模糊。
    【解决方案2】:

    这可能是指一个编写错误的编译器,它未能插入括号来消除错误消息的歧义。具体来说,该函数需要一个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 特有的奇怪类型错误。至少,没有一个像给出的例子。我怀疑奥拉只是在吹烟。

    【讨论】:

      【解决方案3】:

      由于许多函数式语言允许您以与重新绑定变量相同的方式重新绑定类型名称,因此实际上很容易出现这样的错误,特别是如果您对类型使用了一些通用名称(例如,@987654321 @) 在不同的模块中。这是 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-21
        • 2021-11-06
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 2019-10-16
        相关资源
        最近更新 更多