OCaml,类似于Emacs的程序扩展

4
我很喜欢OCaml,我正在等待我的《Real World OCaml》!我是一个初学者,只了解函数式部分,略懂命令式,但对于模块、函数子、对象等并不熟悉。
为了一个解释器项目,我创建了一个类似Emacs的新手评估。我保留了一个三元组列表,其中包含一组命令名称绑定字符串、一个描述字符串和要调用的OCaml函数。主循环只是在列表中查找匹配的条目并调用函数。
然后添加新功能非常简单,只需编写一个函数并在列表中放置一个条目即可。
我喜欢像Emacs一样具有自扩展性的概念,这很容易扩展,但不是真正的自扩展。 我能用OCaml使程序具有自扩展性吗?我该怎么做? 我知道Emacs是一个大型虚拟机,因此它通过自身解释代码并修改其运行时环境来实现,但是否有一种方法可以通过用户添加的模块或其他方式向OCaml程序添加功能?
附注:如果我的项目对您来说看起来很基础,请不要嘲笑我,因为我是一个初学者!
谢谢
2个回答

5
另一个解决方案可能是使用Ocaml的动态加载功能,例如Dynlink模块。每个动态加载的共享模块可以向某个全局哈希表中添加条目,将字符串名称映射到函数(相同类型),等等...这个初始化-运行其顶级表达式-在动态加载模块的加载时间发生(当您调用Dynlink.loadfile时),有点像POSIX dlopen的旧_init函数。
例如,您可以让程序在运行时发出一些Ocaml代码;通过fork编译成可加载的动态库使用ocamlopt -shared;然后Dynlink.loadfile该库。该库的初始化部分将使用主程序中提供的适当函数注册闭包。
或者,编写(或使用)一些在Ocaml中的虚拟机或解释器。

你也可以使用一些JIT库,例如Ollvm,并生成一些类似C的代码(可能在主程序的一些C粘合代码中使用dlopen)。

但正如Simon Shine回答的那样MetaOcaml可能是更好的选择。

顺便说一下,也许你想要一些Common Lisp实现?


示例:

(未经测试的代码!某些细节可能不正确)

您的主程序prog.ml将包含以下内容:

let ht = Hashtbl.create 53;;
let add_name_fun (name : string) (f : int -> int) = 
   Hashtbl.add ht name f;;
(* here you might emit the 'plugin.ml' file and fork its compilation *) 
Dynlink.loadfile "plugin.cmxs";;
(* as an example we apply every added name & function to 3 *)
Hashtbl.iter ht (fun n f) -> Printf.printf "n=%s (f 3)=%d\n" n (f 3);;

您的 prog.mli 文件应包含以下内容:

  val : add_name_fun : string -> (int -> int) -> unit;;

您的plugin.ml文件将包含以下内容:

  Prog.add_name_fun "foo" (fun x -> x+3);;

但是我没有测试这段代码。

附注:在POSIX系统上,你可以使用C语言的dlopendlsym来做类似的事情。我的GCC MELT正在做这个。还可以参见这个

NB:如果你喜欢元编程方法,请阅读J.Pitrat的博客和书籍。


谢谢您。我正在考虑实现Dynlink的方式,如果实现得好的话,它可能会起作用,就像您说的那样,它可能不安全...但这是一个很好的选择,我也会采纳!此外,BER MetaOCaml非常吸引人! 由于我在大学学习了OCaml并希望“掌握”它(程序员永远不会掌握一种语言...),所以我更喜欢深入研究OCaml。但是当我有时间时,我肯定会去看看Common Lisp。 - Nicolas Scotto Di Perto
这个问题的目的是为了一个 Xmonad 窗口管理器的分支项目(用 Haskell 编写),这次使用 OCaml 和 wayland,以便学习。用户可以在运行时添加一些用 OCaml 代码编写的功能(就像 Xmonad 中的 Haskell 一样),而无需重新加载所有配置,就像 Emacs 一样。 - Nicolas Scotto Di Perto
在 Dynlink 函数 loadfile 中: "加载的编译单元中的所有顶层表达式都将被评估。没有提供访问单元定义的值名称的工具。因此,单元必须通过修改函数表将其入口点与主程序注册,例如。" 它是如何工作的?当我调用 loadfile 时,它会运行 ocaml 代码并修改函数入口吗?如果有一些非声明性代码,那么会执行吗? 连接的代码可以访问加载器的变量吗? 如果可以的话,我可以拥有一个函数列表并将加载的函数添加到该列表中。 - Nicolas Scotto Di Perto
是的,您可能需要一个从字符串名称到函数的哈希表。 - Basile Starynkevitch
好的,现在更清楚了...http://zderadicka.eu/plugins-in-ocaml-with-dynlink-library/是一个关于加载插件的好教程。所以我可以有一个插件接口,加载插件模块并将它们存储在一个带有文件名哈希的关联列表中。我是对的吗? - Nicolas Scotto Di Perto
显示剩余4条评论

2

我能否使用OCaml实现自扩展程序?

可以,您可以制作一个自扩展的解释器。

是否有一种方法可以通过用户添加模块将功能添加到OCaml程序中?

可以,但是。OCaml交互式提示符是这样一个可扩展的程序。MetaOCaml是OCaml的多阶段编程扩展,可以在运行时逐步编译新的机器码。[维基百科] 除非您依靠其中之一,否则您需要完成一个相当复杂的工程任务。

我该怎么做?

根据您想要做什么的具体情况,您还可以查看OCaml作为Scheme标准ML中的Scheme解释器。然后,您基本上需要构建一个读取-评估-打印循环,该循环解析输入语言并相应地修改三元组。但不直接使用OCaml。


MetaOcaml似乎是我正在寻找的东西。 但我不确定,我想让用户在运行时添加代码到程序中。代码将从文件中读取,肯定是字符串形式,我能用MetaOcaml将字符串转换为代码吗? - Nicolas Scotto Di Perto
像eval函数一样,MetaOCaml似乎并不像那样工作,仍然有编写内部解释器的解决方案,但它会失去很多功能性。 - Nicolas Scotto Di Perto
有人使用OCaml toplevel库(https://thelackthereof.org/OCaml_Eval)编写了一个eval函数,但它在实际版本的OCaml上无法工作,它使用的文件toplevellib.cma尚不存在,太糟糕了... - Nicolas Scotto Di Perto

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