FSet maps and seqs both allow you to specify the value that will be returned by a lookup when
the key or index is outside the domain of the collection. This is a handy feature that
significantly improves the elegance of certain kinds of code: you specify the default once, when
creating the collection, rather than every place you access it. Contrast a Lisp alist, where every
access would look like (or (cdr (assoc x alist)) default), or even a CL
hashtable, where you’d have to supply the default as the third argument to every call to
gethash.
For convenience, and consistency with CL, the default default — the value used as a default if you
don’t specify one — is good old nil. In a few cases, however, such as compose of a
map with a function, the presence of any default is annoying, because the function being composed
with has to accept the default value and produce a default for the result map. So FSet also allows
maps and seqs to have no default. When this option is used, lookup on the collection,
given a key or index outside its domain, will signal an error. You can use this as a convenient
defensive programming mechanism in cases where you don’t expect out-of-domain lookups.
When a map or seq is printed that has a default other than nil, the default is printed at the
end, separated by a slash; when it has no default, the string /[no default] is appended.
Map defaults are handy when you’re using chained maps, as discussed in the previous section, since
they let you arrange for the inner maps to be created automatically as needed. This is done by
giving the outer map a default which is the empty map. Then you can use the setf expression
shown in the previous section without further ado:
(let ((m (empty-map :default (empty-map)))) ... (setf (@ (@ m k1) k2) value) ...)
If you were doing something like this with CL hash tables, you would have to explicitly check,
before doing the setf, that the outer map had an entry for k1, and if not, initialize
it to the empty map; roughly:
(mvlet ((inner found? (gethash k1 m))) (unless found? (setf inner (make-hash-table)) (setf (gethash k1 m) inner)) (setf (gethash k2 inner) value))
With FSet, the default on the outer map makes the explicit check unnecessary; the empty map is used
automatically if m has no entry yet for k1.