Haskell 正在抛出异常w/‘永远’?

Haskell 正在抛出异常w/‘永远’?,haskell,Haskell,永远看着,: λ: import Control.Monad λ: :t forever forever :: Monad m => m a -> m b 为什么forever$Just 42会在几秒钟后抛出异常 λ: forever $ Just 42 *** Exception: <<loop>> λ: forever $ return 4 ^C^C^C^Z [1]+ Stopped ghci 为了不深入讨论实现的细

永远看着

λ: import Control.Monad
λ: :t forever
forever :: Monad m => m a -> m b
为什么
forever$Just 42
会在几秒钟后抛出异常

λ: forever $ Just 42
*** Exception: <<loop>>
λ: forever $ return 4
^C^C^C^Z
[1]+  Stopped                 ghci

为了不深入讨论实现的细节(我并不完全熟悉),我将解释为什么需要这种行为以及它的来源

永久
定义为

forever x = do
    x
    forever x
这相当于

forever x = x >> forever x
所以如果我们看看

fr x = forever (return x)
我们有

fr x = forever (return x)
     = return x >> forever (return x)
     = return x >>= \_ -> fr x
     = (\_ -> fr x) x
     = fr x
所以,基本上,
frx
是一个无限循环

现在在Haskell中,无限循环和错误是相同的。以下是
错误的一种可能实现方式

error s = error s
现在,GHC选择区分这两种类型的循环

纯代码中的无限循环是一个错误。一般来说,我们并不打算输入一个,我们正在寻找一个值,而不是一个副作用。因此,在
中,一个无限循环可能以错误结束

然而,有效代码中的无限循环是常见的!代码可能在等待机器上发生其他事情,或者用户输入,或者其他事情


因此,在
IO
中,我们有一个实际的无限循环,一个有意义的循环。

需要明确的是:在语义上,异常和无限循环是不可区分的,因为它们都被赋予了底部的表示。因此,从语义上讲,
永远(返回0)::Maybe()
永远(返回0)::IO()
。GHC使用称为“灰洞”的东西来处理并发上下文中的惰性值。为了避免重复大量代码,它还在单线程运行时中使用它们,为了进行调试,它们被变形为“黑洞”,有时让您知道自己处于无限循环中。它必须是正确的无限循环类型,而且不能保证。