在标准 ML 中声明相互依赖的值和函数

Declaring interdependent values and functions in Standard ML

如何在标准 ML 中定义相互依赖的函数和值

以下程序:

val cmds = [("help", cmd_help)];

fun cmd_help () = List.app (fn cmd => print (#1 cmd ^ "\n")) cmds;

cmd_help ();

不编译:

$ mlton example.sml
Error: example.sml 1.22-1.29.
  Undefined variable: cmd_help.

是否可以定义 cmdscmd_help,使它们彼此了解并作为顶级定义暴露给程序的其余部分?

有趣的问题,最明显的方法是使 cmds 成为单一函数,然后在 cmd_help 中调用它,就像一对普通的递归函数一样。

fun cmds () = [("help", cmd_help)]
and cmd_help () = List.app (fn cmd => print ((#1 cmd) ^ "\n")) (cmds ());
cmd_help ();

人们会期望您可以扭转这种情况,将 cmds_help 视为一个值。事实上,语法甚至似乎可以解析。

val a = ()
and b = fn () => ()

但是,当您将相互递归添加到作品中时,它实际上并没有编译(我尝试了以下 MLtonsmlnj ).手动声明类型,因为推理不是特别好。

val cmds_v : (string * (unit -> unit)) list = [("help", cmd_help_v)]
and cmd_help_v : unit -> unit = fn () => List.app (fn (cmd: string * (unit -> unit)) => print ((#1 cmd) ^ "\n")) (cmds_v);

最后一个案例的结果是:

Error: unbound variable or constructor: cmds_v

Error: unbound variable or constructor: cmd_help_v

所以现在的问题是,为什么这行不通 如果我们查看第 42 页的 Value Bindings 部分 The Definition of Standard ML (Revised) 在脚注 25 中它说

(25) When the option is present we have Dom VE ∩ Dom VE′ = ∅ by the syntactic restrictions.

因此,虽然函数的 and 允许相互递归,但值的 and 允许确保值环境不相交。

唉,如果不将值提升为函数,我不知道该怎么做。

我认为最好的解决方案是将函数包装在 ref 访问中,如下所示:

val cmd_help_ref : (unit -> unit) ref
    = ref (fn _ => raise Fail "cmd_help not bound");

fun cmd_help () = !cmd_help_ref ();

val cmds = [("help", cmd_help)];

cmd_help_ref := fn () => List.app (fn cmd => print (#1 cmd ^ "\n")) cmds;

cmd_help ();