Recently, I've been often nesting several functions that return multiple values. However, unlike let
, which me allows to write these calls elegantly into one big statement, I always end up with a lot of indentation.
My question is: having several multiple-valued functions such as
(defun return-1-and-2 ()
(values 1 2))
(defun return-3-and-4 ()
(values 3 4))
is it possible to achieve the same as
(multiple-value-bind (one two)
(return-1-and-2)
(multiple-value-bind (three four)
(return-3-and-4)
(list one two three four)))
but write it more concisely the let
-way, i.e., something like
(multiple-let (((one two) (return-1-and-2))
((three four) (return-3-and-4)))
(list one two three four))
?
Probably there are similar constructs in libraries.
Note that it is more similar to let*
, not let
, since scope is nested.
One could write a macro. For example:
(defmacro multiple-value-let* ((&rest bindings) &body body)
"Sets the scope for several ((var-0 ... var-n) form)
binding clauses, using the multiple return values of the form."
(if (null bindings)
`(progn ,@body)
(destructuring-bind (((&rest vars) form) &rest rest-bindings)
bindings
`(multiple-value-bind ,vars
,form
(multiple-value-let* ,rest-bindings
,@body)))))
Example:
CL-USER 33 > (walker:walk-form
'(multiple-value-let* (((one two) (return-1-and-2))
((three four) (return-3-and-4)))
(list one two three four)))
(MULTIPLE-VALUE-BIND (ONE TWO)
(RETURN-1-AND-2)
(MULTIPLE-VALUE-BIND (THREE FOUR)
(RETURN-3-AND-4)
(PROGN (LIST ONE TWO THREE FOUR))))
I have grown a bit fond of the library let-plus
, which offers a let+
macro that has this option (among others):
(let+ (((&values one two) (return-1-and-2))
((&values three four) (return-3-and-4))
(foo (bar)) ; other examples
(#(a b c) (some-vector))) ;
#| body… |#)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With