Haskell的类型推理奇怪

Iva*_*lov 6 haskell type-inference ghc ghci monomorphism-restriction

看看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
Run Code Online (Sandbox Code Playgroud)

为什么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
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

我发现这种变化编译得很好(类型定义相同):

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
Run Code Online (Sandbox Code Playgroud)

经过一些实验后发现,如果我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
Run Code Online (Sandbox Code Playgroud)

这引出了我的第一个问题.

bdo*_*lan 7

默认情况下,除非给出显式类型说明符,否则顶级绑定是非多态的; 这被称为" 单态限制 ".由于没有给出类型说明符,GHC必须选择一种k在定义函数时实例化的方法.碰巧选择了k = ().

这背后的想法是多态性可能通过在最终编译的代码中引入大量的vtable调用来损害性能; 通过强制在编译时解析这些,除非另有明确说明,否则可以避免这种开销.这个决定颇具争议.GHC支持通过传递完全禁用单态限制的扩展-XNoMonomorphismRestriction.

  • 请注意,正常的默认不会在整个地方设置为"()"; 在GHCi中使用了扩展的默认模式. (6认同)
  • "这背后的想法是多态性可能通过在最终编译代码中引入大量vtable调用来损害性能"这些不是问题 - 或者至少不是最大的问题.引入单同性限制的主要原因是,如果没有它,某些表达式将在你不期望它们时被多次评估. (3认同)
  • 或者通过在源文件中添加注释`{ - #LANGUAGE NoMonomorphismRestriction# - }`(仅用于完整性). (2认同)
  • 我一直在闲逛太久了...我读了问题的标题,在我点击链接之前认为"必须是单态的". (2认同)
  • 顺便说一句,虽然对于编译的源文件存在合理的争论,但实际上你几乎从未想过GHCi中的单态限制.每个人都应该将`:set -XNoMonomorphismRestriction`添加到他们的`〜/ .ghci`文件中. (2认同)