问题
There seems to be syntactical restrictions within SML's recursive bindings, which I'm unable to understand. What are these restrictions I'm not encountering in the second case (see source below) and I'm encountering when using a custom operator in the first case?
Below is the case with which I encountered the issue. It fails when I want to use a custom operator, as explained in comments. Of the major SML implementations I'm testing SML sources with, only Poly/ML accepts it as valid, and all of MLton, ML Kit and HaMLet rejects it.
Error messages are rather confusing to me. The clearest one to my eyes, is the one from HaMLet, which complains about “illegal expression within recursive value binding”.
(* A datatype to pass recursion as result and to arguments. *)
datatype 'a process = Chain of ('a -> 'a process)
(* A controlling iterator, where the item handler is
* of type `'a -> 'a process`, so that while handling an item,
* it's also able to return the next handler to be used, making
* the handler less passive. *)
val rec iter =
fn process: int -> int process =>
fn first: int =>
fn last: int =>
let
val rec step =
fn (i: int, Chain process) (* -> unit *) =>
if i < first then ()
else if i = last then (process i; ())
else if i > last then ()
else
let val Chain process = process i
in step (i + 1, Chain process)
end
in step (first, Chain process)
end
(* An attempt to set‑up a syntax to make use of the `Chain` constructor,
* a bit more convenient and readable. *)
val chain: unit * ('a -> 'a process) -> 'a process =
fn (a, b) => (a; Chain b)
infixr 0 THEN
val op THEN = chain
(* A test of this syntax:
* - OK with Poly/ML, which displays “0-2|4-6|8-10|12-14|16-18|20”.
* - fails with MLton, which complains about a syntax error on line #44.
* - fails with ML Kit, which complains about a syntax error on line #51.
* - fails with HaMLet, which complains about a syntax error on line #45.
* The clearest (while not helpful to me) message comes from HaMLet, which
* says “illegal expression within recursive value binding”. *)
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
val () = iter process 0 20
val () = print "\n"
(* Here is the same without the `THEN` operator. This one works with
* all of Poly/ML, MLton, ML Kit and HaMLet. *)
val rec process =
fn x =>
(print (Int.toString x);
Chain (fn x => (print "-";
Chain (fn x => (print (Int.toString x);
Chain (fn x => (print "|";
Chain process)))))))
val () = iter process 0 20
val () = print "\n"
(* SML implementations version notes:
* - MLton, is the last version, built just yesterday
* - Poly/ML is Poly/ML 5.5.2
* - ML Kit is MLKit 4.3.7
* - HaMLet is HaMLet 2.0.0 *)
Update
I could work around the issue, but still don't understand it. If I remove the outermost parentheses, then it validates:
val rec process: int -> int process =
fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process)))
Instead of:
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
But why is this so? An SML syntax subtlety? What's its rational?
回答1:
It's just an over-restrictive sentence in the language definition, which says:
For each value binding "pat = exp" within rec, exp must be of the form "fn match".
Strictly speaking, that doesn't allow any parentheses. In practice, that's rarely a problem, because you almost always use the fun declaration syntax anyway.
来源:https://stackoverflow.com/questions/25858856/sml-syntactical-restrictions-within-recursive-bindings