haskell - 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 产生同样的错误。
这也发生在以下变体中:
使用
where
main :: IO () main = output [ s 42, s True ] where s = show
使用
let ... in
main :: IO () main = let s = show in output [ s 42, s True ]
但在这三种情况下,替换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 = succ
为s x = succ x
仍然可以解决问题。
我无法为这种意外行为找到解释。这是一个错误吗?如果不是,请解释发生了什么。
解决方案
你被单态限制所困扰,它不是一个错误。该页面的前两段:
“单态限制”是 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 提示符下被禁用,并且仅适用于已编译的模块。
推荐阅读
- docker - 为什么 Metricbeat 正在运行但不提供任何指标?
- javascript - MongoDb - 如何以 ObjectId("") 作为值创建有效的 JSON/Javascript 对象?
- clickhouse - 计算结果的物化视图
- azure - 如何访问包含来自 Azure Log Analytics 查询的数据的 azure 数据库
- apache-kafka - 并行从单个流主题写入不同的主题
- node.js - 如何将输入字段映射为mongodb中的不同字段
- html - 我如何始终保持我的 html 元素粘在父元素的底部,我正在使用溢出:隐藏并将高度隐式地赋予父元素?
- java - @Validated 注解使@ConfigurationProperties 类的字段为空
- powershell - 在线交换,New-DynamicDistributionGroup 与电话号码
- javascript - ajax php mysql 需要多少数据?