将我的运行中的toplevel保存以备后用

29

在使用 ocamlghci 解释器时,我经常会建立一个重要的“上下文”(我没有更好的词来形容),包括绑定的值、函数、已加载的模块等。有没有一种方法可以将所有内容保存并在以后重新加载,以便我可以恢复到之前的状态?或者更好的是,将整个内容导出为文本文件,以便重新加载或轻松修改为编译成可执行文件的代码(例如通过添加一个 Main 函数)。


1
我曾多次渴望过这一刻。不断地设置“上下文”有时让我发疯。 - Daniel
1
注意:我的赏金是针对Haskell的最佳答案,无论是ghci还是hugs。 - A. Rex
6个回答

11
HOL Light的用户有类似的需求,他们使用一个检查点程序来保存顶层的快照。请参见caml邮件列表上这条消息这本HOL教程的第8页。
通常情况下,最好将定义保留为源代码,而不是二进制的顶层快照。许多工具允许快速将.ml文件加载到顶层进行轻松实验(如emacs模式等)。请参见HOL教程中的警告:
“在开发HOL中的大型证明时,您应始终将证明脚本保存为可重新加载的OCaml文件,而不是依赖于ckpt。这将允许后来修改证明,并由其他人使用。但是,制作中间快照非常方便,这样您就不必每次在证明上继续工作时都要加载大型文件。这类似于编程中的通常情况:您应始终保留完整的源代码,但不想每次使用该代码时都重新编译所有源文件。”

10

至少在OCaml中,没有内置支持来实现这一点。一个解决方案是使用 rlwrap 或任何其他readline包装器将输入历史记录到文件中。例如:

> rlwrap -H mysession.ml ocaml

缺点是这样也会记录语法错误的输入,因此您需要清除它们。请注意,默认情况下,如果不使用-H选项调用它,rlwrap将自动将您的输入保存在~/.ocaml_history中。


我已经在我的shell中定义了一个别名,oc='rlwrap ocaml',所以这很容易做到,并且可以让我回到使用#use时的状态... 我想要的是shell中“env”命令的等效命令,它可以转储出所有环境变量。 - Gaius

10
在 Haskell 中,只需使用 :e 文件名 命令。这会打开标准编辑器并让您编辑某个文件。之后使用 :r 命令重新加载它。文件将被自动重新编译。
请注意,使用此方法定义的所有“临时”函数都将丢失。有关更多信息,请参阅文档。

1
另一种选择是在Emacs的haskell-mode中工作,编辑文件并使用C-c C-l将其发送到下位进程。如果这是双向的,那就完美了。 - Gaius

7

ghci使用haskeline来记录命令行输入历史,因此您可以向上滚动以重复/编辑输入。您的输入历史通常记录在一个文件中,您可以在给定目录中找到名为ghci_history的文件。

System.Directory.getAppUserDataDirectory "ghc"

有各种命令可以探索“上下文”(:show bindings、:show modules、:def等),但它们的输出不足以重现您的会话(尽管了解它们也是值得的)。

一般来说,将您的ghci会话与打开的编辑器窗口结合使用的建议很好:如果它不仅仅是一个丢弃的定义,即使只是为了调试目的,最好将其包含在一个模块中,以便可以将其重新使用到ghci中。

哦,如果您所说的“上下文”是指每个项目基础上要加载的一些默认设置或模块,则还有ghci配置文件。也很方便定义自己的ghci命令。


7
在OCaml中,您可以构建自己的顶级。这至少解决了加载模块的问题。 http://caml.inria.fr/pub/docs/manual-ocaml/toplevel.html#sec278

The ocamlmktop command builds OCaml toplevels that contain user code preloaded at start-up.

The ocamlmktop command takes as argument a set of .cmo and .cma files, and links them with the object files that implement the OCaml toplevel. The typical use is:

    ocamlmktop -o mytoplevel foo.cmo bar.cmo gee.cmo

This creates the bytecode file mytoplevel, containing the OCaml toplevel system, plus the code from the three .cmo files. This toplevel is directly executable and is started by:

    ./mytoplevel

This enters a regular toplevel loop, except that the code from foo.cmo, bar.cmo and gee.cmo is already loaded in memory, just as if you had typed:

    #load "foo.cmo";;
    #load "bar.cmo";;
    #load "gee.cmo";;

on entrance to the toplevel. The modules Foo, Bar and Gee are not opened, though; you still have to do

    open Foo;;

yourself, if this is what you wish.


我使用了那种技术,我在Jane Street博客上看到了一篇关于构建“脚本化”顶层的文章,其中包含最有用/相关的内容。但我考虑的用例是探索性/实验性工作,我希望能够保留它以防重新启动,或者将其转移到另一台机器上并从上次离开的地方继续进行。 - Gaius
链接失效了。下次请引用相关内容。 - Cees Timmerman

5

这个问题一直困扰着我,所以我写了一个快速的python/expect 脚本,在每个ghci会话开始时重新播放ghci_history。

它并不是很完美。例如,它总是重新播放整个历史记录,可能会很慢。


在会话过程中,还存在着这样一个风险:有时候你可能执行了一个不想重复的“:! ”。 - Gaius
1
可能应该过滤掉以下命令:___。当然,你不能保护所有情况。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接