I like the idea of LAND*; I think I'd use a form like this often.
The syntax proposed is clean and clear. Thank you, Oleg!
However, the specification of LAND* in the draft SRFI is perhaps not
as clear as it could be. I think it's important that the initial
SRFI's set a high standard for future authors to follow, so I hope
Oleg will not be offended if I explain how I think draft SRFI 2 might
be improved.
The notation used to describe the LAND* syntax is unfamiliar; it
resembles BNF, but uses `cons' in a way I've never seen in any other
BNF spec. Whenever possible, I think SRFI's should use existing,
well-defined, and widely recognized notations for describing syntax;
when that is not possible, the SRFI itself should explain the notation
used.
The notation used to describe LAND*'s semantics is also unfamiliar.
I would suggest that SRFI's should imitate R5RS as closely as
possible, when R5RS provides appropriate examples. In the present
case, R5RS provides excellent precedents to follow.
Thus, looking at how R5RS handles `cond', I would suggest a definition
consisting of:
- syntax, specified informally, using templates with English restrictions,
- semantics, specified informally, in English, and
- for derived expression types, a macro definition using R5RS's
define-syntax. (Note that using R5RS macros does *not* constrain an
implementation to provide LAND* using R5RS macros, or macros of any
sort; R5RS macros are simply a well-defined, widely recognized
notation for describing the syntax and semantics of expressions.)
For example:
Syntax: (land* (<clause> ...) <body> ...)
Each <clause> should have one of the following forms:
(<variable> <expression>)
(<expression>)
<bound-variable>
Each <variable> or <bound-variable> should be an identifier. Each
<expression> should be a valid expression. The <body> should be a
possibly empty sequence of expressions, like the <body> of a
lambda form.
Semantics: A LAND* expression is evaluated by evaluating the
<expression> or <bound-variable> of each of the <clause>s from
left to right. The value of the first <expression> or
<bound-variable> that evaluates to a false value is returned; the
remaining <expression>s and <bound-variable>s are not evaluated.
The <body> forms are evaluated iff all the <expression>s and
<bound-variable>s evaluate to true values.
The <expression>s and the <body> are evaluated in an environment
binding each <variable> of the preceding (<variable> <expression>)
clauses to the value of the <expression>. Later bindings
shadow earlier bindings.
(define-syntax land*
(syntax-rules ()
((land* () body ...)
(begin body ...))
((land* ((var expr) clauses ...) body ...)
(let ((var expr))
(if var (land* (clauses ...) body ...) #f)))
((land* ((expr) clauses ...) body ...)
(if expr (land* (clauses ...) body ...) #f))
((land* (var clauses ...) body ...)
(if var (land* (clauses ...) body ...) #f))))
(Note that the define-syntax definition above is untested.)
Some further comments:
I find the name `land*' a bit obscure; is it `logical and'? Or
perhaps `dry land'? Something like `and-let*' might be nicer. I
think we should follow the example of `set-car!', not `rplaca'.
Isn't a <clause> of the form <bound-variable> equivalent to one of the
form (<bound-variable>)? The fact that the list contains only one
element shows that no binding should be created. Thus,
<bound-variable> clauses seem unnecessary. I wouldn't mind writing
(lambda (x y)
(land* ((first (car x))
(y))
(do-something first)))
instead of
(lambda (x y)
(land* ((first (car x))
y)
(do-something first)))
But this is not a big deal.