letv
2024-10-12
The LETV Package. Exports two macros, LETV and LETV* that allow to combine standard LET and LET* constucts with MULTIPLE-VALUE-BIND in a possible less verbose way that also requires less indentation.
The LETV
and LETV*
Macros
Marco Antoniotti <mantoniotti [at] common-lisp.net>
2023-03-09
See the file COPYING
for copyright and licensing information.
This little hack introduces two handy Common Lisp macros, LETV
and
LETV*
that allow you to mix regular LET
and MULTIPLE-VALUE-BIND
forms in a less verbose way. The amount of indentation needed is
also reduced.
The syntax of LETV
(or LETV*
) is very "loopy", with a nod to
SML/OCaml/F#/Haskell/Julia. The current syntax is the following:
letv ::= 'LETV' [vars] [IN [body]]
letvstar ::= 'LETV*' [vars] [IN [body]]
vars ::= var | var vars
var ::= ids '=' <form> [decls]
decls ::= decl | decl decls
decl ::= OF-TYPE idtypes
ids ::= <symbol> | '(' <symbol> + ')'
idtypes ::= <type designator> | '(' <type designator> + ')'
body ::= [<declarations>] <form> *
(I know: the grammar is not completely kosher, but I trust you will understand it.)
The two macros expand in forms that mimic the semantics of LET
,
LET*
and MULTIPLE-VALUE-BIND
. All type declarations, if present,
are properly handled via LOCALLY
forms.
LETV
expands into a LET
, with variable initialized by PSETQ
s and
MULTIPLE-VALUE-SETQ
s.
LETV*
expands into a form that is an interleaving of LET*
and
MULTIPLE-VALUE-BIND
.
Examples
- Simple case:
(letv x = 42 in (format t "The answer is ~D.~%" x))
- Same with declaraion:
(letv x = 42 of-type fixnum in (format t "The answer is ~D." x))
- Simple case with
MULTIPLE-VALUE-BIND
:(letv (v found) = (gethash 'the-key *my-hash-table*) in (if found (format t "Found THE-KEY, doing stuff.~%") (error "THE-KEY not found.")))
- Mixing things up:
(letv (v found) = (gethash 'the-key *my-hash-table*) of-type (fixnum boolean) x = 42 in (if found (format t "Found THE-KEY, adding to the answer ~D.~%" (+ v x)) (error "THE-KEY not found.")))
- With
LETV*
:(letv* (v found) = (gethash 'the-key *my-hash-table*) of-type (fixnum boolean) x = (when found (+ v 42)) in (if found (format t "Found THE-KEY, adding to the answer ~D.~%" x) (error "THE-KEY not found.")))
- The other way around.
(letv x = 42 of-type integer (v found) = (gethash 'the-key *my-hash-table*) of-type (fixnum boolean) in (if found (format t "Found THE-KEY, adding to the answer ~D.~%" (+ v x)) (error "THE-KEY not found.")))
- A more compelling example.
(letv* (p found) = (gethash 'the-key *points-table*) of-type (point) (x y) = (if found (unpack-point p) (values :missing :missing)) in ;; Adding declarations here also works. (declare (type point p) (type (or real (eql :missing)) x y)) (do-stuff-with x y) (do-things-with p))
All the examples are meant to illustrate the use of LETV
and
LETV*
.
Notes
LETV
and LETV*
are obviously not the first macros of this kind
floating around: others are available and all have their niceties. But
I never claimed not to suffer form NIH syndrome.
LETV
and LETV*
do not do "destructuring" or "pattern matching".
That is a different can of worms; but you can check
cl-unification for a library
(always by yours truly) that provides facilities in that sense.
A NOTE ON FORKING
Of course you are free to fork the project subject to the current licensing scheme. However, before you do so, I ask you to consider plain old "cooperation" by asking me to become a developer. It helps keeping the entropy level at an acceptable level.