6

我一直在使用uncurryGHCi 中的函数,但我发现了一些我根本无法理解的东西。当我应用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' = (+)

注意:我无法使用已编译的文件来重现它

4

2 回答 2

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)

一切都很好,正如预期的那样。单态限制拒绝使“看起来像一个值”(即在等号的左侧没有参数定义)类型类多态,因为这会破坏通常会发生的缓存。例如。

foo :: Integer
foo = expensive computation

bar :: (Num a) => a
bar = expensive computation

foo保证只计算一次(好吧,至少在 GHC 中),而bar每次提到它时都会计算。单态性限制旨在通过在看起来像您想要的那样时默认为前者来将您从后一种情况中拯救出来。

如果您只使用该函数一次(或始终使用相同的类型),类型推断将负责为您推断出正确的类型。在这种情况下,ghci 通过更早地猜测来做一些稍微不同的事情。但是在两种不同的类型上使用它显示了正在发生的事情。

如有疑问,请使用类型签名(或使用 关闭可怜的东西{-# LANGUAGE NoMonomorphismRestriction #-})。

于 2011-02-15T04:24:49.200 回答
4

使用 ghci 扩展默认规则是有魔力的。基本上,除其他外,Num 约束默认为 Integer,Floating 约束默认为 Double,否则会出现错误(在这种情况下,由于邪恶的单态限制)。

于 2011-02-15T01:16:56.810 回答