为什么年份=年+ 1因堆栈溢出而失败?

时间:2014-12-31 21:16:37

标签: haskell ghc tail-recursion

year.hs:

year = year + 1

main = print year

这不是尾递归调用:

year = year + 1
year = (year + 1) + 1
year = ((year + 1) + 1) + 1
...

但是runhaskell year.hs没有输出任何内容,这表明它会遇到无限循环。

Haskell编译器是否对非尾递归调用进行优化?

1 个答案:

答案 0 :(得分:19)

由于懒惰(加上单态限制)。基本上当你说

year = year + 1

然后评估year,Haskell为结果保存空间,然后当它看到year时,它会尝试重复使用相同的结果。因此,当year + 1尝试评估year时,year的代码实际上并未输入。

在GHC中,要实现多线程,它实际上做的是在尝试获取已经被评估的变量的值时阻止当前线程。然后,当评估完成时,它将继续执行被阻止的线程。在这种情况下,线程在它自己正在进行的评估上被阻塞,这就是你遇到死锁的原因。

如果您改为说

year () = year () + 1

然后运行year ()会给我一个堆栈溢出。


单态限制发挥作用,因为如果你添加一个类型签名

year :: Num a => a
year = year + 1

编译器可以完全自由地将Num a字典视为()参数,从而产生堆栈溢出。在这种情况下,这不是一个真正的问题,但不缓存中间结果是实际计算中的一个大问题。在这种情况下,看起来GHC实际上将递归放在抽象的字典中,产生更像

的代码
year () = let y = y + 1 in y

也不会产生堆栈溢出。

<小时/> 在单线程模式下编译代码(使用GHC)会产生

<<loop>>

这意味着GHC检测到无限循环,并决定抱怨它。