2

您如何在标准 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.

是否可以定义cmdsandcmd_help使得它们都相互了解并且也作为顶级定义暴露给程序的其余部分?

4

2 回答 2

3

有趣的问题,最明显的方法是创建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);

最后一种情况导致:

错误:未绑定的变量或构造函数:cmds_v

错误:未绑定的变量或构造函数:cmd_help_v

所以现在的问题是,为什么这不起作用 如果我们看一下Value Bindings部分,在脚注 25 的The Definition of Standard ML (Revised) 的第 42 页上, 它说

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

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

唉,我不知道如何在不提升函数值的情况下做到这一点。

于 2020-08-16T11:01:43.173 回答
1

我认为最好的解决方案是将函数包装在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 ();
于 2021-01-08T22:36:56.657 回答