Haskell 的类型推断奇怪之处
查看 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
经过一些实验后发现,如果我输入 type of 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
这引出了我的第一个问题。
Look at this output from 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
Why look
's inferred type differs from type of flip Data.Map.lookup
?
To give you some context. Initially I had small program and was trying to figure out why it produces compiler error:
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's reaction:
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'
I've found that this variation compiles well (type definitions are the same):
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
And after some experimentation it's turned up that if I put type of look
explicitly - first version compiles well too:
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
Which leads me to my first question.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
默认情况下,除非给出显式类型说明符,否则顶级绑定是非多态的;这称为“单态限制”。由于没有给出类型说明符,GHC 必须在定义函数时选择一种实例化
k
的方法。它恰好选择了k = ()
。这背后的想法是,多态性会在最终编译的代码中引入大量 vtable 调用,从而损害性能;除非另有明确说明,否则通过强制在编译时解决这些问题,可以避免这种开销。这个决定颇具争议。 GHC 支持扩展来禁用通过传递 -XNoMonomorphismRestriction 来完全限制单态。
By default, top-level bindings are non-polymorphic unless an explicit type specifier is given; this is known as the 'monomorphism restriction'. Since no type specifier was given, GHC had to choose a way to instantiate
k
at the time you defined the function. It happened to choosek = ()
.The idea behind this is that polymorphism can hurt performance by introducing a lot of vtable calls in the final compiled code; by forcing these to be resolved at compile time unless otherwise explicitly stated, this overhead can be avoided. This decision is rather controversial. GHC supports an extension to disable the monomorphism restriction entirely, by passing
-XNoMonomorphismRestriction
.