如何停止GHCi中的无限评估? [英] How can I stop infinite evaluation in GHCi?

查看:65
本文介绍了如何停止GHCi中的无限评估?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我运行类似的内容时:

When I run something like:

Prelude> cycle "ab"

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

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"

我无法阻止它.

问题:

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

更新:

 Ubuntu: version 18.10  
 GHCi:   version 8.2.2

推荐答案

好问题!但是,由于如何在GHCI中中止执行?已经着重于第二部分,我们在此不再赘述.相反,让我们专注于第一个.

Great question! However, since How to abort execution in GHCI? already focuses on your second part, let's not repeat that here. Instead, let's focus on the first.

为什么会这样?

Why it so?

GHC积极优化循环.如果没有分配

GHC optimizes loops aggressively. It optimizes them even further if there is no allocation that it's even a known bug:

19.2.1. GHC中的错误

19.2.1. Bugs in GHC

  • GHC’s runtime system implements cooperative multitasking, with context switching potentially occurring only when a program allocates. This means that programs that do not allocate may never context switch. This is especially true of programs using STM, which may deadlock after observing inconsistent state. See Trac #367 for further discussion. [emphasis mine]

如果遇到此问题,则可能需要使用

If you are hit by this, you may want to compile the affected module with -fno-omit-yields (see -f*: platform-independent flags). This flag ensures that yield points are inserted at every function entrypoint (at the expense of a bit of performance).

如果我们检查

If we check -fomit-yields, we find:

-fomit-yields

默认:启用屈服点

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

Tells GHC to omit heap checks when no allocation is being performed. While this improves binary sizes by about 5%, it also means that threads run in tight non-allocating loops will not get preempted in a timely fashion. If it is important to always be able to interrupt such threads, you should turn this optimization off. Consider also recompiling all libraries with this optimization turned off, if you need to guarantee interruptibility. [emphasis mine]

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

nub $ cycle "ab" is a tight, non-allocating loop, although last $ repeat 1 is an even more obvious non-allocating example.

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

The "yield points enabled" is misleading: -fomit-yields is enabled by default. As the standard library is compiled with -fomit-yields, all functions in the standard library that lead to tight, non-allocating loops may show that behaviour in GHCi, as you never recompile them.

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

We can verify that with the following program:

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

main = print $ myLast $ repeat 1

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

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中重新运行它,它将挂起:

If we compile it and then rerun it in GHCi, it will hang:

$ 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中).

Note that you need -dynamic if you don't use Windows, as otherwise GHCi will recompile the source file. However, if we use -fno-omit-yield, we suddenly can quit again (in Windows).

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

We can verify that again with another small snippet:

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编译的.

As ghci doesn't use any optimizations, it also doesn't use -fomit-yield (and therefore has -fno-omit-yield enabled). Our new variant of last doesn't yield the same behaviour as Prelude.last as it isn't compiled with fomit-yield.

现在我们知道发生这种情况的原因了,我们知道随着标准库使用-fomit-yield编译,我们将在整个标准库中遇到这种行为.

Now that we know why this happens, we know that we will experience that behaviour throughout the standard library, as the standard library is compiled with -fomit-yield.

这篇关于如何停止GHCi中的无限评估?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆