Haskell 为什么';t年=年+;1因堆栈溢出而失败?

Haskell 为什么';t年=年+;1因堆栈溢出而失败?,haskell,ghc,tail-recursion,Haskell,Ghc,Tail Recursion,年份.hs: year = year + 1 main = print year 这不是尾部递归调用: year = year + 1 year = (year + 1) + 1 year = ((year + 1) + 1) + 1 ... 然而,runhaskell year.hs没有输出任何东西,这表明它运行在无限循环中 Haskell编译器是否对非尾部递归调用进行优化?因为惰性(加上单态限制†)。基本上当你说 year = year + 1 然后计算year,Haskell会为结

年份.hs:

year = year + 1

main = print year
这不是尾部递归调用:

year = year + 1
year = (year + 1) + 1
year = ((year + 1) + 1) + 1
...
然而,
runhaskell year.hs
没有输出任何东西,这表明它运行在无限循环中

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

因为惰性(加上单态限制†)。基本上当你说

year = year + 1
然后计算
year
,Haskell会为结果节省空间,然后当它看到
year
时,它会尝试重新使用相同的结果。因此,当
year+1
尝试评估
year
时,
year
的代码实际上不会被输入

在GHC中,要实现多线程‡,它实际上是在试图获取已被计算的变量的值时阻塞当前线程。然后,当评估完成时,它将继续执行被阻塞的线程。在本例中,线程在其自身执行的计算中被阻塞,这就是为什么会出现死锁

如果你说

year () = year () + 1
然后运行
year()
确实会给我带来堆栈溢出


†单态限制开始发挥作用,因为如果您添加类型签名

year :: Num a => a
year = year + 1
编译器完全可以像对待
()
参数那样处理
Num
字典,从而产生堆栈溢出。在这种情况下,这不是一个真正的问题,但在实际计算中,不缓存中间结果是一个大问题。在本例中,看起来GHC实际上是将递归放在字典的抽象中,生成更像

year () = let y = y + 1 in y
这也不会产生堆栈溢出。
&达格尔;在单线程模式下编译代码(使用GHC)会产生

<<loop>>


这意味着GHC检测到无限循环并决定对此进行投诉。

注意内存使用情况。这取决于优化,但这是可能发生的一件事。假设的输出是什么?我假设结果是未定义的。@Comm假设的输出是
异常:堆栈溢出
澄清一下:
年份的类型默认为
Int
,还是我弄错了?@ThreeFx听起来很合理,但我现在不能查。不过这并不重要,因为您可以给
year
任何单态类型签名(例如,
year::Int
year::Integer
year::Float
,等等)(只要该类型具有
Num
实例),而不改变行为(只要该类型的
+
是严格的)@ThreeFx您可以更改整数和小数文本的默认类型,但默认类型是(整数,双精度)。只需添加
year::Num a=>a
,即可得到相同的结果。还是没有堆栈overflow@orionll,刚试过,你是对的。看起来GHC确实优化了这种情况(将递归放在字典抽象中),因此即使使用多态类型签名,也可以获得懒惰行为。我会在有机会的时候编辑我的答案。