Haskell 的类型推断奇怪之处

发布于 2024-11-27 09:49:24 字数 2046 浏览 0 评论 0原文

查看 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尘世孤行 2024-12-04 09:49:24

默认情况下,除非给出显式类型说明符,否则顶级绑定是非多态的;这称为“单态限制”。由于没有给出类型说明符,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 choose k = ().

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文