let+
: destructuring extension of let*
This library implements the let+
macro, which is a dectructuring extension of let*
.
&ign
for ignored values (in forms where that makes sense)This library was inspired by 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).
In my opinion the main advantages of this library, compared to metabang-bind, are the placeholder macros which provide editor hints and the more consistent syntax 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, and the library should be easier to extend.
You can find other pattern matching libraries on cliki.
where
binding ::= symbol || (form [init-form])
LET+
is recursive: each binding is in the scope of the previous ones. Forms ignore &ign
variables (where applicable).
Forms which provide both read-write and read-only access are available as &form
and &form-r/o
. The first one always uses symbol macros, so you can use setf
. The second one reads the values at the beginning of the list from value: you can change these variables after that without having any effect on the original value. Read-only forms may also provide a slight increase in speed, and promote good style — you can use them to signal that you will not change the original structure.
The following forms are defined:
var
, (var)
, (var value)
let*
.
(list value)
list
is not recognized as any of the forms below, it is simply destructured using destructuring-bind
. &ign
are ignored. Example:
(let+ (((a (b &optional (c 3)) &ign &key (d 1 d?)) '(1 (2) 7 :d 4))) (list a b c d d?)) ; => (1 2 3 4 T)
((&slots slot*) value)
, also &slots-r/o
with-slots
, each slot
has the syntax variable
or (variable)
(for these, the variable name is also used for the slot name) or (variable slot-name)
. &slots-r/o
provides read-only bindings.
Example:
(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)
((&accessors accessor*) value)
, also &accessors-r/o
&slots
, but uses accessors. Continuing the example above:
(let+ (((&accessors a (b b-accessor)) (make-instance 'foo-class :a 1 :b 2))) (list a b)) ; => (1 2)
((&structure conc-name slot*) value)
, also &structure-r/o
Conc-name
is prepended to the accessors (you need to include the -
if there is one). Example:
(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)
((&values value*) form)
multiple-value-bind
. &ign
are ignored. Example:
(let+ (((&values a &ign b) (values 1 2 3))) (list a b)) ; => (1 3)
(array value)
(only read-only version)
&ign
are ignored. Indexes use row-major access, determined at macroexpansion time. Example:
(let+ ((#(a &ign b) (vector 1 2 3))) (list a b)) ; => (1 3)
((&array-elements (variable subscript*)*) value)
, also &array-elements-r/o
(let+ (((&array-elements (a 0 1) (b 2 0)) #2A((0 1) (2 3) (4 5)))) (list a b)) ; => (1 4)
((&flet name lambda-list forms*))
, also &labels
&labels
allows the function to refer to itself – note that since let+
is always recursive, this is the only difference between the two forms. Example:
(let+ (((&flet add2 (x) (+ x 2)))) (add2 5)) ; => 7
((&plist (variable key [default])*)
, also &plist-r/o
key
is not given, variable
is used instead, and default
is used if the element does not exist in the value (note that default may be evaluated multiple times when using the read-write form which uses symbol-macrolet
). Example:
(let+ (((&plist a (my-b b) (c nil 3)) '(a 1 b 2))) (list a my-b c)) ; => (1 2 3)
(((&hash-table (variable key [default])*)
, also &hash-table-r/o
&plist
.
(&complex real imaginary)
You can nest let+
expressions when it makes sense (it doesn’t always, especially for read/write slots, the read only form should work). For example,
(let+ ((#((&complex a b)) (vector (complex 1 2)))) (list a b))
should destructure the complex number that is the single element in the vector.
If you find that let+
does not nest properly, please report it as a bug.
(defun+ name (argument*) form*)
, also (lambda (argument*) form*)
defun
and lambda
, but arguments are destructured using let+
. Example:
(defun+ foo ((&plist a b c) #(d e)) (list a b c d e)) (foo '(a 1 b 2 c 3) #(4 5)) ; => (1 2 3 4 5)
See also &labels+
and &lambda+
.
define-structure-let+
(&once-only symbols ...)
and (&with-gensyms symbols)
are useful for writing macros.Extending let-plus
is very easy: if you want to use a form that resembles a list, you just have to define a method for let+-expansion-for-list
. There is a macro that helps you with that, called define-let+-expansion
. If the library didn’t have &complex
, we could define destructuring for the form like this:
(define-let+-expansion (&complex (x y)) "Access real and imaginary part of the value. Read-only." `(let ((,x (realpart ,value)) (,y (imagpart ,value))) ,@body))
Some highlights:
&complex
that should help with editor hints, but has no other purpose (it is not used in the expansion),value
(the value form) and body
(the body inside the let+
form), you can customize both of this using keyword arguments,value
is wrapped in once-only
preventing multiple evaluations of the same form. See the arguments :uses-value?
and :once-only?
for define-let+-expansion
.If you want to extend let+
with forms that are not lists (eg like the array syntax above), have a look at let+-expansion
.
Please open an issue on Github for bugs. Extensions are also welcome, either as forks or small code snippets submitted as issues. Wishlist items are also welcome!
I ask you not to report bugs via e-mail if you can avoid it. Tracking bugs on Github makes it less likely that they get lost.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4