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

2024-04-08

当我运行类似的东西时:

Prelude> cycle "ab"

I can see an infinite printing of "ab". To stop it I just use Ctrl+c. And it works.

当我跑步时:

Prelude Data.List> nub $ cycle "ab"

我无法阻止它。

问题:

  • 为什么会这样呢?
  • 我怎样才能停止这个操作?

Update:

 Ubuntu: version 18.10  
 GHCi:   version 8.2.2

好问题!然而,自从如何在 GHCI 中中止执行? https://stackoverflow.com/questions/30877019/how-to-abort-execution-in-ghci已经重点介绍了第二部分,我们在这里不再重复。相反,让我们关注第一点。

为什么会这样呢?

GHC 积极优化循环。如果没有分配,它会进一步优化它们这甚至是一个已知的错误 https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/bugs.html#bugs-in-ghc:

19.2.1。 GHC 中的错误

  • GHC 的运行时系统实现了协作式多任务处理,只有在程序分配时才可能发生上下文切换。这意味着不分配的程序可能永远不会进行上下文切换。对于使用 STM 的程序尤其如此,在观察到不一致的状态后可能会出现死锁。 See 追踪 #367 https://ghc.haskell.org/trac/ghc/ticket/367以供进一步讨论。 [强调我的]

    如果您遇到此问题,您可能需要使用以下命令编译受影响的模块-fno-omit-yields https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-optimisation.html#ghc-flag--fomit-yields (see -f*:与平台无关的标志 https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-optimisation.html#options-f)。该标志确保在每个函数入口点插入让出点(以牺牲一点性能为代价)。

如果我们检查-fomit-yields https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-optimisation.html#ghc-flag--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

We can use C-c to quit it if we run it in GHCi without compiling beforehand:

$ 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>

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

我们可以用另一个小片段再次验证这一点:

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

As ghci不使用任何优化,它也不使用-fomit-yield(因此有-fno-omit-yield已启用)。我们的新变种last不会产生与以下相同的行为Prelude.last因为它不是用编译的fomit-yield.

现在我们知道了why发生这种情况时,我们知道我们将在整个标准库中经历这种行为,因为标准库是用-fomit-yield.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何停止 GHCi 中的无限评估? 的相关文章

随机推荐