首页 > 解决方案 > GHC 8 - 具有重命名函数的约束类型参数化规则

问题描述

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

考虑以下代码:

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 = shows 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 = succs x = succ x仍然可以解决问题。

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

标签: haskellghctype-parameter

解决方案


被单态限制所困扰,它不是一个错误。该页面的前两段:

“单态限制”是 Haskell 类型推理中的反直觉规则。如果您忘记提供类型签名,有时此规则会使用“类型默认”规则将自由类型变量填充为特定类型。结果类型签名的多态性总是比您预期的要少,因此在您期望它为多态表达式推断出完全合理的类型的情况下,这通常会导致编译器向您抛出类型错误。

一个简单的例子是 plus = (+)。如果没有明确的 plus 签名,编译器将不会推断类型 (+) :: (Num a) => a -> a -> a for plus,但会应用默认规则来指定 plus :: Integer -> Integer ->整数。当应用于 plus 3.5 2.7 时,GHCi 将产生看起来有点误导的错误,没有由文字 '3.5' 引起的 (Fractional Integer) 的实例。

只需(+)在此处替换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不允许您将其与数字一起使用。

当应用单态限制时,确切的规则在Haskell Report 2010 的第 4.5.5 节中定义,但相当技术性。

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


推荐阅读