看看ghci的这个输出:
Prelude> :t Data.Map.lookup
Data.Map.lookup :: Ord k => k -> Data.Map.Map k a -> Maybe a
Prelude> :t flip Data.Map.lookup
flip Data.Map.lookup :: Ord a => Data.Map.Map a a1 -> a -> Maybe a1
Prelude> let look = flip Data.Map.lookup
Loading package array-0.3.0.2 ... linking ... done.
Loading package containers-0.4.0.0 ... linking ... done.
Prelude> :t look
look :: Data.Map.Map () a -> () -> Maybe a
为什么look
的推断类型与flip Data.Map.lookup
的类型不同?
给你一些背景信息。最初我有一个小程序,并试图找出它产生编译器错误的原因:
import qualified Data.Map as M
type A = String
type B = String
data C = C1 | C2 | C3
deriving (Eq, Ord)
type D = String
z :: A -> M.Map A B -> M.Map B C -> M.Map C D -> Maybe D
z a aToB bToC cToD = look aToB a >>= look bToC >>= look cToD
where look = flip M.lookup
Ghci的反应:
Prelude> :load main.hs
[1 of 1] Compiling Main ( main.hs, interpreted )
Failed, modules loaded: none.
main.hs:10:52:
Couldn't match expected type `C' with actual type `[Char]'
Expected type: C -> Maybe D
Actual type: A -> Maybe a0
In the return type of a call of `look'
In the second argument of `(>>=)', namely `look cToD'
我发现这种变化编译得很好(类型定义相同):
x :: A -> M.Map A B -> M.Map B C -> Maybe C
x a aToB bToC = look aToB a >>= look bToC
where look = flip M.lookup
y :: A -> M.Map A B -> M.Map B C -> M.Map C D -> Maybe D
y a aToB bToC cToD = (x a aToB bToC) >>= look cToD
where look = flip M.lookup
经过一些实验后发现,如果我明确地输入look
类型 - 第一个版本编译得很好:
z :: A -> M.Map A B -> M.Map B C -> M.Map C D -> Maybe D
z a aToB bToC cToD = look aToB a >>= look bToC >>= look cToD
where look :: (Ord a) => M.Map a b -> a -> Maybe b
look = flip M.lookup
这引出了我的第一个问题。
答案 0 :(得分:7)
默认情况下,除非给出显式类型说明符,否则顶级绑定是非多态的;这被称为'monomorphism restriction'。由于没有给出类型说明符,GHC必须选择一种在定义函数时实例化k
的方法。碰巧选择了k = ()
。
这背后的想法是多态性可能通过在最终编译代码中引入大量vtable调用来损害性能;通过强制在编译时解析这些,除非另有明确说明,否则可以避免这种开销。这个决定颇具争议。 GHC支持an extension完全禁用单态限制,方法是传递-XNoMonomorphismRestriction
。