这是由于two
的类型。让我们检查一下到目前为止的所有类型:
ghci> :t foo
foo :: Foo
ghci> :t two
two :: Num a => a
Aha! two
是多态的。因此,它的行为取决于actual Num
实例。因此,需要重新评估g
。我们可以通过使用来检查这一点:sprint
:
ghci> :sprint foo
foo = Foo _ -- simplified
这表明我们从未查看过内容Foo
. foo
is in 弱头正常形态 https://wiki.haskell.org/Weak_head_normal_form。这回答了你的第二个问题。但回到你的第一个。发生了什么:sprint two
?
ghci> :sprint two
two = _
正如你所看到的,由于其多态性,two
没有到达 WHNF。毕竟,which需要什么时间?你可能想用它作为Integer
, or Currency
, or Complex Triple
.
顺便说一句,这是单态限制存在的原因,请参阅“Haskell 的历史”,第 6.2 节 http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-haskell/history.pdf:
6.2 单态限制
早期阶段争议的一个主要来源是所谓的
“单态限制。”假设genericLength
有
这个重载类型:
genericLength` :: Num a => [b] -> a
现在考虑这个定义:
f xs = (len, len)
where
len = genericLength xs
看起来 len 应该只计算一次,但实际上可以计算两次。为什么?因为我们可以推断类型len :: (Num a) => a
;当用字典脱糖时-
通过翻译,len
成为一个被调用一次的函数
对于每次出现len
,其中每一个都可能在不同的地方使用
类型。
也可以看看有关限制的更多信息,请参阅此问答 https://stackoverflow.com/questions/32496864/what-is-the-monomorphism-restriction.
话虽这么说,如果我们修复的话,我们可以很容易地改变它two
's type:
ghci> let foo = trace "foo" Foo (trace "bar" Bar 100)
ghci> let two = trace "two" (2 :: Integer)
ghci> let g (Foo x) y = y
现在输出将完全符合您的预期。或者,您可以启用单态限制:set -XMonomorphismRestriction
,因为它在当前 GHCi 版本中默认被禁用:
ghci> :set -XMonomorphismRestriction
ghci> let two = trace "two" 2
ghci> :t two
two :: Integer -- due to defaulting rules