【问题标题】:Type inference in GHCi vs. manual signatureGHCi 中的类型推断与手动签名
【发布时间】:2011-12-14 15:41:00
【问题描述】:

当我输入时

:t map length . sum

进入 GHCi,它说类型是:

map length . sum :: Num [[a]] => [[[a]]] -> [Int]

但是,如果我创建一个文件 type-test.hs 包含

x :: Num [[a]] => [[[a]]] -> [Int]
x = map length . sum

ghc 和 ghci 都抱怨:

type-test.hs:1:1:
    Non type-variable argument in the constraint: Num [[a]]
    (Use -XFlexibleContexts to permit this)
    In the type signature for `x': x :: Num [[a]] => [[[a]]] -> [Int]

当未启用 FlexibleContexts 时,为什么 ghci 允许我推断它的类型(使用 :t)?

【问题讨论】:

    标签: haskell type-inference ghc ghci


    【解决方案1】:

    首先,让我们澄清一件事。如果我们在 GHCi 中定义函数而不是查询类型会发生什么?

    > let x = map length . sum :: (Num [[a]]) => [[[a]]] -> [Int]
    <interactive>:0:9:
        Non type-variable argument in the constraint: Num [[a]]
        (Use -XFlexibleContexts to permit this)
        In an expression type signature: Num [[a]] => [[[a]]] -> [Int]
    

    等等。换句话说,同样的事情。如果我们让 GHCi 推断定义的类型呢?

    > let x = map length . sum
    <interactive>:0:22:
        No instance for (Num [[a0]])
          arising from a use of `sum'
        Possible fix: add an instance declaration for (Num [[a0]])
        In the second argument of `(.)', namely `sum'
        In the expression: map length . sum
    

    这与加载包含没有类型签名的定义的文件所导致的错误大致相同。

    这一切的结果是什么?好吧,想想它告诉你需要什么扩展。 GHC 能够识别类型 的含义,即使它默认拒绝该类型。我几乎不指望 GHC 会根据所使用的扩展组合使用完全不同的类型检查器,因此似乎很容易得出结论,除了相关扩展被禁用之外,违规类型被拒绝。

    GHCi 中的:t 命令不是编译过程的一部分——它是类型检查和推理系统的热线,让您询问假设代码的类型。没有明显的理由让它基于扩展来任意限制自己,当更通用的类型仍然可以提供信息时,与上面的错误消息告诉你 use -XFlexibleContexts to permit this 而不仅仅是 syntax error in type constraint 的原因相同。


    除此之外可能更有趣的是,在某些情况下,编译器会愉快地接受推断的类型,但由于多种原因之一,推断的类型实际上不能显式写出。

    例如,禁用单态限制将允许您的示例推断其类型(匹配 :t 所说的),尽管该类型需要手动编写扩展。

    另一个例子是函数定义的where 子句中的定义,它使用父函数的多态参数。它们自己的类型不是多态的,由外部作用域中接收的参数决定,但父函数签名中的类型变量不在where 子句的作用域¹。可能还有其他例子。

    ¹ 如果需要,可以使用 ScopedTypeVariables 扩展和显式 forall 将父签名中的类型变量带入范围。

    【讨论】:

    • 但是请注意,如果禁用单态限制,ghci&gt; let x = map length . sum 有效,ghci&gt; let x xs = map length (sum xs) 也适用于 MR。第一个的 ghci-error 只是由于尝试根据默认规则对类型进行单态化。
    • @DanielFischer:是的,我一发布就意识到我应该澄清这一点,呵呵。我认为关于不可写类型的观点是一个有趣的附录。
    【解决方案2】:

    (这并没有回答你原来的问题,而是用代码解决了问题)

    这些错误暗示您编写的代码可能不是您的意思。这段代码:

    map length . sum
    

    的意思是“获取我的数字列表,并将其相加,然后计算结果数字的每个元素(??)的长度。”这没有任何意义。

    你的意思可能是:

    sum . map length
    

    这意味着“获取我的列表,计算每个元素的长度,然后对长度求和。”

    错误消息本身的意思是因为sum返回一个数字,也就是sum的类型是Num n =&gt; [n] -&gt; n,然后你尝试在上面使用map length,它的类型是Num m =&gt; [[a]] -&gt; [m],编译器试图说 n == [[a]] 使类型匹配,然后一切都从那里走下坡路。

    【讨论】:

      【解决方案3】:

      另一种看待它的方式:

      Haskell 告诉你你的表达式有一个有效的类型,即[[[a]]] -&gt; [Int] 如果只有[[a]]Num 的一个实例。

      这就像一些女孩告诉你她会在地狱结冰时和你一起出去。你不会抱怨她答应和我出去尽管地狱可以冻结这个世界上没有可能性,对吗?你宁愿注意到她只是以或多或少的礼貌方式说

      【讨论】:

      • `[[a]]`` 可以通过许多不同的有用方式成为 Num 的实例。例如。 [[Int]] 可能是一个(不安全的)整数矩阵,因此 hell freezes over 参数不成立。
      • 吹毛求疵的西里尔教授:请注意拥有Num [[Int]]并不意味着拥有Num [[a]]
      • @Ingo,我们也没有Num a,但这不会使Num a =&gt; a 等类型的值无用。大声笑@伟大的比喻,顺便说一句! :D @nponeccop,我敢肯定也有一种(可能不安全的)方法可以改变地狱的气候!
      【解决方案4】:

      问题是该函数完全可以接受,但是语言语法不允许指定其最通用的类​​型。在这种情况下ghci 能做什么?我只能想到两个选项,要么给出一个只能在启用某些扩展的情况下指定的类型,要么给出一个错误。给出一个错误而不提及可以通过启用扩展来修复它对我来说似乎不是很可取。只是让:t 报告推断的最通用类型更简单,而且很可能在实现该功能时,没有人想到这种情况。

      【讨论】:

        【解决方案5】:

        【讨论】:

        • 我不确定类型默认与这里的关系如何...?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-29
        • 1970-01-01
        相关资源
        最近更新 更多