Why another destructuring library?
This Common Lisp library extends the syntax of
let*. In that
respect it is pretty similar to other destructuring libraries, most
importantly Gary King's excellent metabang-bind. I have been using
the latter for years now, but at some point I decided to write a
library of my own, aiming for a cleaner syntax, more concise
implementation and a more consistent interface (whether I have
succeeded is of course a matter of judgement — try metabang-bind to
see if you like it better).
This library differs from metabang-bind in the following ways:
Placeholder macros like
&slots, mainly for providing editor hints (completion, eg syntax reminders in the Emacs status bar with SLIME, displaying of arguments, etc).
More consistent naming of destructuring forms. In particular, when
both read-write and read-only forms are available the latter always
&labelsresemble the Common Lisp syntax more closely.
- The code is extremely simple (< 300 loc, not counting unit tests), and the library should be easier to extend.
Lists are destructured unless recognized as something else (values
NIL are ignored):
(let+ (((a (b &optional (c 3)) nil &key (d 1 d?)) '(1 (2) 7 :d 4))) (list a b c d d?)) ; => (1 2 3 4 T)
Slots and accessors have a syntax similar to
(defclass foo-class () ((a :accessor a :initarg :a) (b :accessor b-accessor :initarg :b))) (let+ (((&slots a (my-b b)) (make-instance 'foo-class :a 1 :b 2))) (list a my-b)) ; => (1 2) (let+ (((&accessors a (b b-accessor)) (make-instance 'foo-class :a 1 :b 2))) (list a b)) ; => (1 2)
Slots in structures can be accessed by giving the
(defstruct foo-struct c d) (let+ (((&structure foo-struct- c (my-d d)) (make-foo-struct :c 3 :d 4))) (list c my-d)) ; => (3 4)
but if you use
defstruct+ to define your structure, you
automatically get a corresponding deconstructing form:
(defstruct+ interval left right) (let+ ((interval (make-interval :left 1 :right 2)) ((&interval left right) interval)) (incf right 10) interval) ; => #S(INTERVAL :LEFT 1 :RIGHT 12)
Multiple values are also supported:
(let+ (((&values a nil b) (values 1 2 3))) (list a b)) ; => (1 3)
You can access array elements in many different ways:
(let+ ((#(a nil b) (vector 1 2 3))) (list a b)) ; => (1 3) (let+ (((&array-elements (a 0 1) (b 2 0)) #2A((0 1) (2 3) (4 5)))) (list a b)) ; => (1 4)
You can use
&labels, if the function needs to refer to
itself) to write functions:
(let+ (((&flet add2 (x) (+ x 2)))) (add2 5)) ; => 7
&hash-tables allow access to elements property lists
and hash tables.
All of the
&... forms have a read-only version, eg
With these, the values are read at the beginning, and you can modify
them without modifying the original structure. Read-only forms may
also give you a slight increase in speed, and promote better style by
indicating that you are not modifying the original structure.