close

#CommonLisp #Lisp #Alexandria

Someone asked a question on #lisp on Freenode: since WHEN-LET has an analogous WHEN-LET*, why IF-LET has no analogous IF-LET*?

Here is my attempt at answering it.


Suppose we have a following code block:

(if-let ((x 42) 
         (y nil)
         (z :foo))
  (list :true x y z)
  (list :false x y z))

All of X, Y, and Z are bound to the respective values: 42, NIL, and :FOO. At least one of the values is false. Therefore, it should be obvious that the result of evaluating this code would be the list (:FALSE 42 NIL :FOO).

Now, let us slightly modify this code:

(if-let* ((x 42) 
          (y nil)
          (z :foo))
  (list :true x y z)
  (list :false x y z))

We no longer use IF-LET but instead we use the hypothetical sequential version, IF-LET*. We evaluate X and Y. By definition, we are supposed to not evaluate the following variables at this moment, since Y evaluated to false, and we instead execute the false branch.

The big question is: what is the value of Z in the false branch?

It is certainly not :FOO because we have not managed to evaluate that binding yet. In fact, that lexical variable should be unbound since there is no logical value for it, but there is no such thing as an unbound lexical variable in Lisp. In particular, this might mean that we encounter any number of logically unbound variables in the false branch. We could work around it by setting the not-yet-bound lexical variables to some predefined value (which would greatly complicate the code and introduce a lot of mess) or we would make the false branch unable to use any of the lexical bindings, which makes the sequential IF-LET* greatly inconsistent with the parallel IF-LET.


In other words: having IF-LET* would be inconsistent. None of the variables are available in the lexical scope of the false branch, and that should be part of IF-LET*'s contract.

  • favorite

    1 Like

    Claes Wallin (韋嘉誠)