macro IF-LET (bindings then-form &optional else-form)
Creates new variable bindings, and conditionally executes either THEN-FORM or ELSE-FORM. ELSE-FORM defaults to NIL.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable-1 initial-form-1) (variable-2 initial-form-2) ... (variable-n initial-form-n))
All initial-forms are executed sequentially in the specified order. Then all the variables are bound to the corresponding values.
If all variables were bound to true values, the THEN-FORM is executed with the bindings in effect, otherwise the ELSE-FORM is executed with the bindings in effect.
-------
macro IF-LET* (bindings then-form &optional else-form)
Creates new variable bindings, and conditionally executes either THEN-FORM or ELSE-FORM. ELSE-FORM defaults to NIL.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable-1 initial-form-1) (variable-2 initial-form-2) ... (variable-n initial-form-n))
Each initial-form is executed in turn, and the variable bound to the corresponding value. Initial-form expressions can refer to variables previously bound by the IF-LET*.
If all variables are true after the bindings are complete, the THEN-FORM is executed with the bindings in effect, otherwise the ELSE-FORM is executed with the bindings in effect.
------
macro WHEN-LET (bindings &body forms)
Creates new variable bindings, and conditionally executes FORMS.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable-1 initial-form-1) (variable-2 initial-form-2) ... (variable-n initial-form-n))
All initial-forms are executed sequentially in the specified order. Then all the variables are bound to the corresponding values.
If all variables were bound to true values, then FORMS are executed as an implicit PROGN.
------
WHEN-LET* (bindings &body forms)
Creates new variable bindings, and conditionally executes FORMS.
BINDINGS must be either single binding of the form:
(variable initial-form)
or a list of bindings of the form:
((variable-1 initial-form-1) (variable-2 initial-form-2) ... (variable-n initial-form-n))
Each initial-form is executed in turn, and the variable bound to the corresponding value. Initial-form expressions can refer to variables previously bound by the WHEN-LET*.
If all variables are true after the bindings are complete, then FORMS are executed as an implicit PROGN.
------
The floor is open,
-- Nikodemus
I am pretty happy with all of these. (Modulo one s/IF-LET*/WHEN-LET*/ typo I already fixed in the docstrings above.)
Short-circuiting the evaluation in IF-LET* and WHEN-LET* has been proposed on occasion. I do think it is better not to, because that complicates the semantics slightly, and also because in the absence of short-circuiting anyone assuming short-circuiting behavior is going to notice the problem pretty much immediately, whereas should someone rely on _not_ short-circuiting, they might not notice the short-circuiting behavior which I can imagine causing subtle bugs. ...but if someone has a compelling use-case for short-circuiting, I am happy to reconsider.
The only other thing that comes to mind are declarations, but for IF-LET and IF-LET* it would make sense to put the declarations between the binding and the IF in the expansion, while it would be far more convenient to use a LOCALLY around the body in WHEN-LET and WHEN-LET* -- but wrong for SPECIAL declarations. I think in cases where declarations are needed, these constructs are liable to be either too easy to misuse, or wrong for some cases -- better to not have them now, IMO. We can always add them later without breaking compatibility if it turns out people need them.
The tests seem to cover the important bases, and the implementation is pretty trivial. Not much to go wrong there.
Cheers,
-- Nikodemus
"Nikodemus Siivola" nikodemus@random-state.net writes:
I am pretty happy with all of these. (Modulo one s/IF-LET*/WHEN-LET*/ typo I already fixed in the docstrings above.)
I'd suggest to throw them away, and implement something like Erik Naggum's WHEREAS outside of Alexandria instead.
Short-circuiting the evaluation in IF-LET* and WHEN-LET* has been proposed on occasion. I do think it is better not to, because that complicates the semantics slightly, and also because in the absence of short-circuiting anyone assuming short-circuiting behavior is going to notice the problem pretty much immediately, whereas should someone rely on _not_ short-circuiting, they might not notice the short-circuiting behavior which I can imagine causing subtle bugs. ...but if someone has a compelling use-case for short-circuiting, I am happy to reconsider.
In my experience short-circuiting is what you actually need far more often.
IIUTC, when short-circuiting,
(when-let ((foo (frob1)) (bar (frob2)) (quux (frob3)) ..body..)
is an abbreviation for
(let ((foo (frob1))) (when foo (let ((bar (frob2))) (when bar (let ((quux (frob3))) (when quux ..body..))))))
Which is very tedious to do manually. The non short-circuting form would be an abbreviation for
(let ((foo (frob1)) (bar (frob2)) (quux (frob3))) (when (and foo bar quux) ..body..))
which is less obnoxious to write manually.
-T.
In my experience short-circuiting is what you actually need far more often.
my 0.02 about short-circuiting: when using a conditional form (like when-let), people should be prepared for some of their code not being evaluated. and you should try to stay sideffect-free anyway, so i vote for short-circuiting.
On Sat, May 31, 2008 at 4:09 PM, Attila Lendvai attila.lendvai@gmail.com wrote:
my 0.02 about short-circuiting: when using a conditional form (like when-let), people should be prepared for some of their code not being evaluated. and you should try to stay sideffect-free anyway, so i vote for short-circuiting.
What would the semantics of IF-LET* be, or should it be then exciced, and short-circuiting left for WHEN-LET*?
That is, would
(if-let* ((foo (foo)) (bar (bar))) (foobar foo bar) (progn (bar (ensure-bar)) (foobar foo bar)))
be legal?
Cheers,
-- Nikodemus
On Sat, May 31, 2008 at 4:03 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
I'd suggest to throw them away, and implement something like Erik Naggum's WHEREAS outside of Alexandria instead.
Can you explain you reasoning?
(when-let ((foo (frob1)) (bar (frob2)) (quux (frob3)) ..body..)
Do you have a use case from real code?
Cheers,
-- Nikodemus
"Nikodemus Siivola" nikodemus@random-state.net writes:
On Sat, May 31, 2008 at 4:03 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
I'd suggest to throw them away, and implement something like Erik Naggum's WHEREAS outside of Alexandria instead.
Can you explain you reasoning?
The reasoning is as following:
a) The macros should short-circuit because if they don't, they're just sugaring over something that can be written directly without much effort or loss of aesthetics (YMMV.)
b) If they short-circuit, Naggum's WHEREAS is the technical superior solution, as it'll also cover MULTIPLE-VALUE-BIND, for instance.
(when-let ((foo (frob1)) (bar (frob2)) (quux (frob3)) ..body..)
Do you have a use case from real code?
Not really, because in real code, matters are more complicated warranting a more complex macro. Hence my pledge for removal.
Do you have a use case for the non short-circuiting behaviour?
-T.
On Sat, May 31, 2008 at 5:17 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
Not really, because in real code, matters are more complicated warranting a more complex macro. Hence my pledge for removal.
Do you have a use case for the non short-circuiting behaviour?
I don't have a _personal_ use-case for * variants at all. My own code use only the unstarred versions.
My most common use-case is
(when-let (x (foo)) (frob x))
replacing
(awhen (foo) (frob it))
.
Cheers,
-- Nikodemus
I've come around.
WHEN-LET* should indeed short-circuit. Because the semantics for IF-LET* are less clear (re. the second leg), I think it should be dropped. Has anyone ever actually needed IF-LET*? I originally added mostly for symmetry.
Cheers,
-- Nikodemus
On Sun, Jun 1, 2008 at 1:09 AM, Nikodemus Siivola nikodemus@random-state.net wrote:
I've come around.
Patch attached.
* Delete IF-LET*.
* Make WHEN-LET* short-circuit, and adjust documentation to suit.
* Test-case.
Unless there are objections, I'll push this soonish.
Cheers,
-- Nikodemus
"Nikodemus Siivola" nikodemus@random-state.net writes:
macro IF-LET (bindings then-form &optional else-form)
I only want to raise the question whether IF-LET should be renamed BIF. (Or perhaps BIF could be added as an alias.) My rationale is that IF-LET throws indentation a bit too far to the right.
(if-let (it (foo)) (do it) (dont-do it))
vs.
(bif (it (foo)) (do it) (dont-do it))
I suspect this name is controversial, though.
On Sun, Jun 1, 2008 at 8:44 PM, Luis Oliveira luismbo@gmail.com wrote:
I suspect this name is controversial, though.
It is. :)
Adding the alias is a one-liner, and BIF is not as guessable as IF-LET, IMO. Personally I would prefer to indent it like this:
(if-let (x (foo)) (bar x) (quux))
...but that may be equally controversial...
Cheers,
-- Nikodemus
"Nikodemus Siivola" nikodemus@random-state.net writes:
(if-let (x (foo)) (bar x) (quux))
...but that may be equally controversial...
I'd like that. But I suppose it'd screw up the lambda list. Perhaps Tobias's upcoming EDITOR-HINTS stuff is the answer here.
Luis Oliveira luismbo@gmail.com writes:
"Nikodemus Siivola" nikodemus@random-state.net writes:
(if-let (x (foo)) (bar x) (quux))
...but that may be equally controversial...
I'd like that. But I suppose it'd screw up the lambda list. Perhaps Tobias's upcoming EDITOR-HINTS stuff is the answer here.
You can get this indentation already with
(defmacro if-let (bindings &body (then-form &optional else-form) ...)
Did you refer to this as screwing up the lambda-list?
-T.
On Mon, Jun 2, 2008 at 9:29 AM, Tobias C. Rittweiler tcr@freebits.de wrote:
You can get this indentation already with
(defmacro if-let (bindings &body (then-form &optional else-form) ...)
Did you refer to this as screwing up the lambda-list?
TCR, you take the cake! This is brilliant! I never realized destructuring applied to &body as well.
Cheers,
-- Nikodemus
On Mon, Jun 2, 2008 at 11:36 AM, Nikodemus Siivola nikodemus@random-state.net wrote:
On Mon, Jun 2, 2008 at 9:29 AM, Tobias C. Rittweiler tcr@freebits.de wrote:
You can get this indentation already with
(defmacro if-let (bindings &body (then-form &optional else-form) ...)
Did you refer to this as screwing up the lambda-list?
TCR, you take the cake! This is brilliant! I never realized destructuring applied to &body as well.
Pushed this, and the short-circuiting WHEN-LET* along with IF-LET* deletion.
Cheers,
-- Nikodemus
On Mon, Jun 2, 2008 at 11:52 AM, Nikodemus Siivola nikodemus@random-state.net wrote:
Pushed this, and the short-circuiting WHEN-LET* along with IF-LET* deletion.
I would be inclined to bless IF-LET, WHEN-LET, and WHEN-LET* as they stand in the repository now. Unless there are objections or vetoes, I'll post the next round of review items tomorrow.
Cheers,
-- Nikodemus
"Tobias C. Rittweiler" tcr@freebits.de writes:
You can get this indentation already with
(defmacro if-let (bindings &body (then-form &optional else-form) ...)
Did you refer to this as screwing up the lambda-list?
No, it didn't occur to me that the &body parameter would be destructurable like every other parameter in a macro lambda list. Thanks!
alexandria-devel@common-lisp.net