我一直在玩GHCi中的uncurry
功能,而且我发现了一些我根本无法获得的功能。当我将uncurry
应用于(+)
函数并将其绑定到某个变量(如下面的代码中)时,编译器会将其类型推断为特定于Integer
:
Prelude> let add = uncurry (+)
Prelude> :t add
add :: (Integer, Integer) -> Integer
但是,当询问下面表达式的类型时,我得到(我期望的)正确的结果:
Prelude> :t uncurry (+)
uncurry (+) :: (Num a) => (a, a) -> a
会导致什么?是GHCi特有的吗?
同样适用于let add' = (+)
。
注意:我无法使用已编译的文件重现该文件。
答案 0 :(得分:21)
这与ghci无关。这是单形态限制令人恼火。如果您尝试编译以下文件:
add = uncurry (+)
main = do
print $ add (1,2 :: Int)
print $ add (1,2 :: Double)
您将收到错误消息。如果你展开:
main = do
print $ uncurry (+) (1,2 :: Int)
print $ uncurry (+) (1,2 :: Double)
正如所料,一切都很好。单态限制拒绝制作“看起来像一个值”的东西(即在equals的左侧没有参数定义)类型多态,因为这会破坏通常会发生的缓存。例如
foo :: Integer
foo = expensive computation
bar :: (Num a) => a
bar = expensive computation
保证 foo
只计算一次(好吧,至少在GHC中),而每次提到时都计算bar
。单态约束试图通过默认为前者来拯救你,使其成为你想要的东西。
如果您仅使用该功能一次(或始终使用相同类型),则类型推断将负责为您推断正确的类型。在那种情况下,ghci通过更快的猜测做了一些稍微不同的事情。但是在两种不同的类型中使用它会显示正在发生的事情。
如有疑问,请使用类型签名(或使用{-# LANGUAGE NoMonomorphismRestriction #-}
关闭可怜的东西)。
答案 1 :(得分:4)
ghci扩展违约规则涉及到魔力。基本上,除其他外,Num约束默认为Integer和Floating约束为Double,否则会出现错误(在这种情况下,由于邪恶的单态限制)。