GHC 8 - 具有重命名函数的约束类型参数化规则

GHC 8 - Constrained type parameterization rules with renamed functions

我对 GHC 在相当简单的 Haskell 程序中出现的看似错误的行为感到困惑。

考虑以下代码:

import System.IO
output :: [String] -> IO()
output stringList = sequence_ $ map putStrLn stringList

main :: IO ()

s = show

main = output [
    s 42,
    s True
  ]

在 GHC 8.4.3 中产生以下输出:

$ runghc parameterize.hs
.hs:9:7: error:
    • No instance for (Num Bool) arising from the literal ‘42’
    • In the first argument of ‘s’, namely ‘42’
      In the expression: s 42
      In the first argument of ‘output’, namely ‘[s 42, s True]’
  |
9 |     s 42,
  |

GHC 8.0.2 产生同样的错误。

以下变体也会发生这种情况:

但是在这三种情况下,将s = show替换为s x = show x就解决了问题:

main :: IO ()

s x = show x

main = output [
    s 42,
    s True
  ]

$ runghc u.hs
42
True

这不是 show 特有的。这是一个失败的 succ 函数在 Enum 元素上运行:

main :: IO ()
main = let s = succ in putStrLn $ showList [
    show $ s 41,
    show $ s False
  ] ""

s x = succ x 替换 s = succ 仍然可以解决问题。

我找不到对这种意外行为的解释。这是一个错误吗?如果不是,请解释发生了什么。

你被 monomorphism restriction 咬伤了,这不是虫子。该页的前两段:

The "monomorphism restriction" is a counter-intuitive rule in Haskell type inference. If you forget to provide a type signature, sometimes this rule will fill the free type variables with specific types using "type defaulting" rules. The resulting type signature is always less polymorphic than you'd expect, so often this results in the compiler throwing type errors at you in situations where you expected it to infer a perfectly sane type for a polymorphic expression.

A simple example is plus = (+). Without an explicit signature for plus, the compiler will not infer the type (+) :: (Num a) => a -> a -> a for plus, but will apply defaulting rules to specify plus :: Integer -> Integer -> Integer. When applied to plus 3.5 2.7, GHCi will then produce the somewhat-misleading-looking error, No instance for (Fractional Integer) arising from the literal ‘3.5’.

只需将此处的 (+) 替换为 show,这就是您的示例。

以这段代码为例:

import System.IO
output :: [String] -> IO()
output stringList = sequence_ $ map putStrLn stringList

main :: IO ()

s = show

s2 x = show x

main = do
  output [
    s False,
    s True
   ]
  output [
    s2 42,
    s2 True
    ]

加载包含以下内容的文件后,您会在 ghci 中得到以下结果:

Prelude> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, one module loaded.
*Main> :t s
s :: Bool -> String
*Main> :t s2
s2 :: Show a => a -> String
*Main>

因此,在您的第一个示例中,为 s 推导的类型不允许您将其与数字一起使用。

此处定义了适用单态限制的确切规则 Section 4.5.5 of Haskell Report 2010,但相当技术性。

另请注意,单态性限制在 ghci 提示符下已停用,仅适用于已编译的模块。