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
have the
-r/o
suffix -
&flet
and&labels
resemble 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.
Examples
Lists are destructured unless recognized as something else (values
mapping to 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 with-slots
:
(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 conc-name
:
(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 &flet
(and &labels
, if the function needs to refer to
itself) to write functions:
(let+ (((&flet add2 (x) (+ x 2)))) (add2 5)) ; => 7
&plist
and &hash-tables
allow access to elements property lists
and hash tables.
Read-only forms
All of the &...
forms have a read-only version, eg &slots-r/o
.
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.
How to get it
The library is a available on Github, under the Boost Software License (Version 1.0). The README file there serves as the documentation, see the source code and unit tests for mode details. As always, bug reports and suggestions are welcome.
Did you think on adding it to Quicklisp?
ReplyDeleteMetabang-bind is very handy specifically because it is available there.
Alkhimov: I asked Zach Beane to add it to Quicklisp, he told me that let-plus will be in the next dist update. He actually found a bug when building for Quicklisp, but I just fixed that.
ReplyDelete