我想制作一个产生Maybe a
的玩具功能,然后抬起show
使其成为Maybe String
,但结果对我来说很奇怪:
λ> :t liftM show . Just
liftM show . Just :: Show a1 => a1 -> Maybe String
λ> liftM show . Just $ 10
Just "10"
λ> let f = liftM show . Just
λ> f 10
<interactive>:9:3:
No instance for (Num ()) arising from the literal `10'
Possible fix: add an instance declaration for (Num ())
In the first argument of `f', namely `10'
In the expression: f 10
In an equation for `it': it = f 10
λ> :t f
f :: () -> Maybe String
λ> let g = liftM show . Just :: (Show a) => a -> Maybe String
λ> :t g
g :: () -> Maybe String
λ> let h = liftM show . Just :: Int -> Maybe String
λ> :t h
h :: Int -> Maybe String
我想它与类型推断有关,但我真的不知道发生了什么:
()
来自哪里?答案 0 :(得分:7)
姆-duuum!
dreaded monomorphism restriction的下一个受害者。
这是怎么回事:对于看起来像“常量变量”的定义(在其他语言也可能使用的意义上,即不是函数类型),如f = ...
,假设你希望它确实表现得像一个常数(CAF,确切地说)。这意味着,它不能是多态的,因为对于参数多态,基本上有一个函数的额外隐式参数(类型a1
应该是的信息)。
要实现这个实际的常量,ghci defaults这个类型变量,它认为最不合适的任何特定类型。这里,唯一的约束是Show
;最简单的类型是()
。
解决这个问题的“正确”方法是关闭单态限制:
前奏&GT; :set -XNoMonomorphismRestriction
前奏&GT; :m + Control.Monad
Prelude Control.Monad&gt;让f = liftM显示。只是
Prelude Control.Monad&gt; f 10
只是“10”左
或者,您可以像在实际的源文件中一样,在ghci中为标识符提供适当的签名:
Prelude Control.Monad&gt;让g :: Show a =&gt; a - &gt;也许String; g = liftM显示。只是
Prelude Control.Monad&gt; g 10
只是“10”左
仅在=
的RHS上执行此操作不起作用,因为单态限制仅在解析后启动并默认输出变量(除非在h
中有首先没有变量,因为你给了RHS一个单态签名。
你可以做的另一件事,只是给函数一个显式参数,然后单态性限制根本不适用。即,将其写成非点免费:
Prelude Control.Monad&gt;让我a = liftM显示$只是一个 Prelude Control.Monad&gt;我10 只是“10”左