It has been a long time since I posted something here. I have been busy with my day job and bogged down in a major rewrite of something (more on this hopefully very soon now (tm)) that is full of rabbit's holes.
I was able to get out of one of these rabbit's holes with this little hack I cooked up that allows you to, possibly, write more concise code.
This little hack introduces two handy Common Lisp
LETV* that allow you to mix
MULTIPLE-VALUE-BIND forms in
a less verbose way. The amount of indentation needed is also reduced.
The syntax of
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
MULTIPLE-VALUE-BIND. All type declarations, if present,
are properly handled via
LETV expands into a
LET, with variable
LETV* expands into a form that is an interleaving of
The library exports only the two symbols
LETV*. The other "symbols" mentioned (
OF-TYPE) are checked as in
LOOP; therefore you can use different styles to write
your code, as you would when writing
The library is available here.
- 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
(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.")))
(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 (declare (type point p) (type real x y)) ; Adding declarations here also works. (do-stuff-with x y) (do-things-with p))
All the examples are meant to illustrate the use of
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 from NIH syndrome.
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