Performance 哈斯克尔';s`seq`对参数进行冗余求值?

Performance 哈斯克尔';s`seq`对参数进行冗余求值?,performance,haskell,Performance,Haskell,如果我正确理解了讨论,seq不应该对一个值求值两次,因为在x`seq`x中,应该对x求值一次 那我为什么会有这种行为呢 λ>:设置+s λ>设fib x=if x fib 30 832040 (2.49秒,638088448字节) λ>设x=fib 30 in x 832040 (2.47秒,638088792字节) λ>设x=fib 30 in x`seq`x 832040 (4.95秒,1276067128字节) 这显然是双重评价?我是不是误解了什么 编辑:正如下面所问的@danidiaz

如果我正确理解了讨论,
seq
不应该对一个值求值两次,因为在
x`seq`x
中,应该对
x
求值一次

那我为什么会有这种行为呢

λ>:设置+s
λ>设fib x=if x fib 30
832040
(2.49秒,638088448字节)
λ>设x=fib 30 in x
832040
(2.47秒,638088792字节)
λ>设x=fib 30 in x`seq`x
832040
(4.95秒,1276067128字节)

这显然是双重评价?我是不是误解了什么

编辑:正如下面所问的@danidiaz,我也进行了评估

λ>(\x->x`seq`x)(fib 30)
832040
(2.51秒,638087888字节)
λ>设x=(fib 30)::x`seq`x中的Int
832040
(2.52秒,732476640字节)

现在更令人惊讶的是


编辑2:我发现这个问题已被标记为先前一个关于单态限制的问题的重复。当我遇到这个问题时,我不知道这是由于限制。因此,如果有人发现他/她处于我的位置,我想这个问题的答案会很有帮助。

对于这个答案的第一部分,
:在ghci中设置-XnomonomoromogismRestriction
。这将在后面解释

天真地说,在Haskell
中,让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的一个特性叫做。与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)::x`seq`x
中的Int,因为我们使用显式类型删除了多态性


还有另一条出路。启用扩展会增加类型默认值的数量,导致
let
表达式比预期的更为单态。在这种情况下,这应该足以恢复共享。

如果您尝试
(\x->x`seq`x)(fib 30)
让x=(fib 30)::x`seq`x中的Int
?添加到post中会发生什么,现在更令人惊讶!问题是
fib
具有多态类型
(orda,numa)=>a->a
。因此,在表达式
x`seq`x
中,第一个
x
可以被实例化为与第二个
x
不同的单态类型。例如,第一个
x
可以是
Int
,而第二个
x
可以是
Double
。没有什么能强迫这两个人成为同一类型。这就是为什么要对
x
进行两次计算的原因。解决方案很简单。给
fib
一个显式的类型签名,比如
let fib::Int->Int;fib x=如果x避免了这种性能问题,那么正是引入单态限制的原因。如果没有MR,
x
是多态的,
x`seq`x
实际上意味着类似于
x()`seq`x()
,就好像
x
是一个函数,并且
seq
将调用该函数两次(可以理解)。
ghci> let f = id in (f True, f 'a')
(True,'a')

ghci> (\f -> (f True, f 'a')) id
*** ERROR ***
fib :: (Ord a, Num a) => a -> a