如何停止GHCi中的无限评估?

时间:2019-03-15 18:32:37

标签: haskell ghci

当我运行类似的内容时:

Prelude> cycle "ab"

我可以看到“ ab”的无限印刷。要停止它,我只使用 Ctrl + c 。而且有效。

当我跑步时:

Prelude Data.List> nub $ cycle "ab"

我无法阻止它。

问题:

  • 为什么会这样?
  • 如何停止此操作?

更新

 Ubuntu: version 18.10  
 GHCi:   version 8.2.2

1 个答案:

答案 0 :(得分:11)

好问题!但是,由于How to abort execution in GHCI?已经专注于您的第二部分,因此在此不再重复。相反,让我们专注于第一个。

  

为什么会这样?

GHC积极优化循环。如果没有分配that it's even a known bug,它将进一步优化它们:

  

19.2.1。 GHC中的错误

     
      
  • GHC的运行时系统实现了协作式多任务处理,仅当程序分配时,上下文切换才可能发生。 这意味着未分配的程序可能永远不会切换上下文。对于使用STM的程序尤其如此,在观察到不一致的状态后,它们可能会死锁。有关进一步的讨论,请参见Trac #367。 [强调我的]

         

    如果对此有疑问,则可能需要使用-fno-omit-yields编译受影响的模块(请参见-f*: platform-independent flags)。此标志可确保在每个函数入口点插入屈服点(以牺牲一些性能为代价)。

  •   

如果我们选中-fomit-yields,则会发现:

  

-fomit-yields

     

默认:启用屈服点

     

告诉GHC在不执行分配时省略堆检查。虽然这将二进制大小提高了约5%,但这也意味着   在紧密的非分配循环中运行的线程不会被抢占   及时地。 如果始终能够打断很重要   这样的线程,您应该关闭此优化。还考虑   如果您关闭此优化功能,则重新编译所有库   需要保证可中断性。 [强调我]

nub $ cycle "ab"是一个紧密的非分配循环,尽管last $ repeat 1是一个更明显的非分配示例。

“启用屈服点”具有误导性:默认情况下启用-fomit-yields。由于标准库是使用-fomit-yields进行编译的,因此标准库中所有导致紧密,非分配循环的函数可能表明GHCi中的行为,因为您无需重新编译它们。

我们可以使用以下程序进行验证:

-- Test.hs
myLast :: [a] -> Maybe a
myLast [x]    = Just x
myLast (_:xs) = myLast xs
myLast _      = Nothing

main = print $ myLast $ repeat 1

如果我们在GHCi中运行它,则可以使用 C-c 退出它,无需事先编译

$ ghci Test.hs
[1 of 1] Compiling Main             ( Test.hs, interpreted )
Ok, one module loaded.
*Main> :main            <pressing C-c after a while>
Interrupted.

如果我们编译它,然后在GHCi中重新运行它,它将挂起:

$ ghc Test.hs
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test.exe ...

$ ghci Test.hs
Ok, one module loaded.
*Main> :main
<hangs indefinitely>

请注意,如果您不使用Windows,则需要-dynamic,否则GHCi将重新编译源文件。但是,如果我们使用-fno-omit-yield,我们突然可以再次退出(在Windows中)。

我们可以使用另一个小片段再次进行验证:

Prelude> last xs = case xs of [x] -> x ; (_:ys) -> last ys
Prelude> last $ repeat 1
^CInterrupted

由于ghci不使用任何优化,因此它也不使用-fomit-yield(因此启用了-fno-omit-yield)。我们的last新变体与Prelude.last的行为不同,因为它不是用fomit-yield编译的。

现在我们知道为什么会发生这种情况,我们知道随着标准库使用-fomit-yield进行编译,我们将在整个标准库中遇到这种情况。