RSS Feed

Lisp Project of the Day


You can support this project by donating at:

Donate using PatreonDonate using Liberapay


This system is highly recommended if you are writing a code which modifies a *readtable* because it allows to define and switch between readtables as you do with Lisp packages.

If you are not familiar with what *readtable* is, then read this article:

but pay attention, that the article manipulates with *readtable* instead of using named-readtables. This is bad. Use named-readtables instead.

First, let's see how to use named-readtables to switch between read-tables. As an example, we'll see how to use cl-interpol and rutils readtables.

This is how you can lookup which tables are available:

POFTHEDAY> (ql:quickload '(:cl-interpol :rutils))

POFTHEDAY> (named-readtables:list-all-named-readtables)

Now let's see how does switching work:

;; First I'll switch to the interpol's syntax:
POFTHEDAY> (named-readtables:in-readtable :interpol-syntax)

POFTHEDAY> (let ((username "Bob"))
             #?"Hello ${username}!")
"Hello Bob!"

;; Rutils readtable is not active, and we can't
;; use it's syntax for hashes:
POFTHEDAY> #h(:foo "bar")
; Debugger entered on #<SB-INT:SIMPLE-READER-ERROR
; "no dispatch function defined for ~S" {10068D4C63}>

;; We have to activate  it first
POFTHEDAY> (named-readtables:in-readtable

POFTHEDAY> #h(:foo "bar")
#<HASH-TABLE :TEST EQL :COUNT 1 {10068B9013}>

;; But now we are unable to use iterpol's syntax:
POFTHEDAY> (let ((username "Bob"))
             #?"Hello ${username}!")
; Debugger entered on #<SB-INT:SIMPLE-READER-ERROR
; "no dispatch function defined for ~S" {1006AE93F3}>

But what if we want to use both readtables from cl-interpol and from rutils?

It is possible if we merge them together and create a new readtable:

POFTHEDAY> (named-readtables:defreadtable

POFTHEDAY> (named-readtables:in-readtable

POFTHEDAY> (let ((username "Bob"))
             #h(:greeting #?"Hello ${username}!"))
#<HASH-TABLE :TEST EQL :COUNT 1 {1003054C23}>

POFTHEDAY> (rutils:print-ht *)
  :GREETING "Hello Bob!"

Now we'll define a literal syntax for lambda from rutils as a separate named read-table:

POFTHEDAY> (defmacro trivial-positional-lambda (body)
             `(lambda (&optional % %%)
                (declare (ignorable %) (ignorable %%))

POFTHEDAY> (defun |^-reader| (stream char)
             (declare (ignore char))
             (let ((sexp (read stream t nil t)))
                 ,(if (and (listp sexp) (listp (car sexp)))
                      (cons 'progn sexp)

POFTHEDAY> (named-readtables:defreadtable
             (:merge :standard)
             (:macro-char #\^ #'|^-reader|))

;; Now we can switch to the new readtable
;; and use new syntax for lambdas:
POFTHEDAY> (named-readtables:in-readtable :lambda)

POFTHEDAY> ^(+ % %%)
#<FUNCTION (LAMBDA (&OPTIONAL % %%)) {2252593B}>

POFTHEDAY> (funcall *

Named readtables has yet another useful feature - it integrates with SLIME. When you have a (in-readtable) call after you package definition, SLIME will know what readtable to use when you hit Ctrl-C Ctrl-C on defuns.

That is what in-readtable expands to:

POFTHEDAY> (named-readtables:in-readtable :interpol-syntax)

;; It expands to:
(eval-when (:compile-toplevel
  (setf *readtable*
  (when (find-package :swank)

This %frob-swank-readtable-alist modifies swank:*readtable-alist* to make it know what readtable should be used for the package. But a comment to this code says it is a KLUDGE.

Interesting, how this will or should work in the LispWorks?

Brought to you by 40Ants under Creative Commons License