我正在尝试编译简单的代码片段.
main = (putStrLn . show) (Right 3.423)
Run Code Online (Sandbox Code Playgroud)
编译导致以下错误:
No instance for (Show a0) arising from a use of `show'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Show Double -- Defined in `GHC.Float'
instance Show Float -- Defined in `GHC.Float'
instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus 42 others
In the second argument of `(.)', namely `show'
In the expression: putStrLn . show
In the expression: (putStrLn . show) (Right 3.423)
Run Code Online (Sandbox Code Playgroud)
当我从ghci执行相同的片段时,一切都按预期工作.
Prelude> let main = (putStrLn . show) (Right 3.423)
Prelude> main
Right 3.423
Run Code Online (Sandbox Code Playgroud)
那么问题是发生了什么?
问题是GHC无法确定完整类型Right 3.423是什么,它只能确定它有类型Either a Double,而Showfor 的实例Either看起来像instance (Show a, Show b) => Show (Either a b).如果没有这个额外的约束Either a Double,GHC不知道如何打印它.
它在交互模式下工作的原因是由于可怕的单态性限制,这使得GHCi在其选择的默认值中更具攻击性.这可以被禁用:set -XNoMonomorphismRestriction,并且这将成为GHC未来版本的默认设置,因为它会给初学者带来很多问题.
此问题的解决方案是Right 3.423在源代码中添加类型签名,例如
main = (putStrLn . show) (Right 3.423 :: Either () Double)
Run Code Online (Sandbox Code Playgroud)
在这里我只是习惯()了a,因为我们无论如何都不关心它,它是可以显示的"最简单"类型.只要它实现,您可以放置String或者Int或者Double任何你想要的东西Show.
小费,putStrLn . show正是定义print,所以你可以做
main = print (Right 3.423 :: Either () Double)
Run Code Online (Sandbox Code Playgroud)
正如@ØrjanJohansen指出的那样,这不是单态限制,而是ExtendedDefaultRulesGHCi使用的扩展,它基本上完全按照我上面所做的那样()推进到类型变量中,使事情在交互式会话中起作用.