• chevron_right

    The case against IF-LET*

    Michał "phoe" Herda · Monday, 15 April, 2019 - 11:49 edit · 1 minute

#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.

  • chevron_right


    Michał "phoe" Herda · Sunday, 23 December, 2018 - 11:53

(This is a repost of an old blog post of mine from Teknik.)

#alexandria #common-lisp

I had a hash table in a running Common Lisp image that I wanted to print readably while preserving object identity (mostly to avoid data duplication).

Alexandria and Bike from #lisp came to the rescue.

;;; (ql:quickload :alexandria)

(defun print-hash-table-readably (hash-table
                                  &optional (stream *standard-output*))
  "Prints a hash table readably using ALEXANDRIA:ALIST-HASH-TABLE."
  (let ((test (hash-table-test hash-table))
        (*print-circle* t)
        (*print-readably* t))
    (format stream "#.(ALEXANDRIA:ALIST-HASH-TABLE '(~%")
    (maphash (lambda (k v) (format stream "   (~S . ~S)~%" k v)) hash-table)
    (format stream "   ) :TEST '~A)" test)