Interesting question, the most obvious way would be to make cmds
a unitary function, and then call that in cmd_help
just like a normal pair of recursive functions.
fun cmds () = [("help", cmd_help)]
and cmd_help () = List.app (fn cmd => print ((#1 cmd) ^ "\n")) (cmds ());
cmd_help ();
One would expect that you could reverse the situation, treating cmds_help
as a value. Indeed the syntax even appears to parse.
val a = ()
and b = fn () => ()
However, it doesn't actually compile when you add mutual recursion into the works (I tried the following with MLton and smlnj). Manually declaring types because the inference wasn't doing particularly well.
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);
This last case results in:
Error: unbound variable or constructor: cmds_v
Error: unbound variable or constructor: cmd_help_v
So the question now is, why doesn't this work
If we look at the section Value Bindings, on pg 42 of
The Definition of Standard ML (Revised)
in the footnote 25 it says
(25) When the option is present we have Dom VE ∩ Dom VE′ = ∅ by the syntactic restrictions.
So while and
for functions allows mutual recursion, and
for values allows ensures that the values environments are disjoint.
Alas, I don't know how to do this without promoting a value to a function.