如果我正确理解了讨论here,则seq
不应该对值进行两次评估,因为x `seq` x
应该评估x
一次。
那我为什么会有这种行为?
λ> :set +s
λ> let fib x = if x <= 1 then x else fib (x - 1) + fib (x - 2)
(0.01 secs, 102,600 bytes)
λ> fib 30
832040
(2.49 secs, 638,088,448 bytes)
λ> let x = fib 30 in x
832040
(2.47 secs, 638,088,792 bytes)
λ> let x = fib 30 in x `seq` x
832040
(4.95 secs, 1,276,067,128 bytes)
这显然是双重评估?我误解了什么吗?
编辑:正如下面的@danidiaz所说,我也评估了
λ> (\x -> x `seq` x) (fib 30)
832040
(2.51 secs, 638,087,888 bytes)
λ> let x = (fib 30) :: Int in x `seq` x
832040
(2.52 secs, 732,476,640 bytes)
现在更令人惊讶。
编辑2:我看到这个问题已被标记为先前问题的副本,该问题询问单态限制。当我遇到这个问题时,我不知道这是由于限制。因此,如果有人发现他/她在我的位置,我想这个问题的答案会有所帮助。
答案 0 :(得分:15)
对于这个答案的第一部分,ghci中的:set -XNoMonomorphismRestriction
。这将在后面解释。
天真地,人们会期望在Haskell中let x = 5 in (x + 1,x + 2)
始终等同于(\x -> (x + 1, x + 2)) 5
。但他们有不同的类型!
let x = 5 in (x + 1,x + 2) :: (Num a, Num b) => (a, b)
(\x -> (x + 1,x + 2)) 5 :: Num b => (b, b)
原因是Haskell的一个功能称为let-bound polymorphism。与lambda绑定标识符不同,绑定在let
中的标识符可以在let
的主体中以不同方式实例化。例如:
ghci> let f = id in (f True, f 'a')
(True,'a')
ghci> (\f -> (f True, f 'a')) id
*** ERROR ***
现在,你没有为你的fib
功能提供类型签名,而推断的那个就是
fib :: (Ord a, Num a) => a -> a
适用于Num
的不同实例,例如Int
,Float
等。
但正因为如此,当你写x `seq` x
时,ghci无法确定两个x
实际上是同一类型!如果他们可能不同,那么他们就无法分享。
这就是(\x -> x `seq` x) (fib 30)
确实有共享的原因。因为x
是lambda绑定的,所以编译器确保两个出现的值实际上是相同的。与let x = (fib 30) :: Int in x `seq` x
相同,因为我们已使用显式类型删除了多态性。
还有另外一条出路。启用-XMonomorphismRestriction
扩展会增加默认类型的数量,从而导致let
表达式比单个预期更加单一。在这种情况下,这应该足以恢复共享。