I extended the above to allow let to handle not just multiple-value-bind
but also labels
. My code is a little simpler than serapeum's since they handle more cool cases than me. For example in serapeum's code, if a let+
has no special features, it expands to a normal let*
. By ignoring those cool features, I get to write it all in just a dozen lines:
(defun %let+ (body xs)
(labels ((fun (x) (and (listp x) (> (length x) 2)))
(mvb (x) (and (listp x) (listp (car x)))))
(if (null xs)
body
(let ((x (pop xs)))
(cond
((fun x) `(labels ((,(pop x) ,(pop x) ,@x)) ,(%let+ body xs)))
((mvb x) `(multiple-value-bind ,(pop x) ,(pop x) ,@(%let+ body xs)))
(t `(let (,x) ,(%let+ body xs))))))))
(defmacro let+ (spec &rest body) (%let+ body spec))
In this let+
macro...
(let+ (x (y 1))...
expands as normal
(let+ ((fn1 (arg1 arg1b) body1))...
wraps fn1
in labels
.
(let+ ((arg2a arg2b) body2))...
does a multiple-value-bind
on body2
, binding its results to arg2a arg2b
.
Example:
(defun fn2 (x y ) (values x (+ x y)))
(defun test-let+(&optional (x 1))
(let+ (z ; normal let stuff
(y 1) ; normal let stuff
(z 2) ; normal let stuff
(fn1 (x y) (+ x y)) ; define a local function
((a b) (fn2 x (fn1 y z)))) ; call multiple-value-bind
(format t "~&a ~a b ~a x ~a y ~a z ~a~%" a b x y z)))
Which expands into this:
(DEFUN TEST-LET+ (&OPTIONAL (X 1))
(LET (Z)
(LET ((Y 1))
(LET ((Z 2))
(LABELS ((FN1 (X Y)
(+ X Y)))
(MULTIPLE-VALUE-BIND (A B)
(FN2 X (FN1 Y Z))
(FORMAT T "a ~a b ~a x ~a y ~a z ~a~%" A B X Y Z)))))))
And runs like this....
> (test-let+)
a 1 b 4 x 1 y 1 z 2
let*
analogy. Is it possible that you have a typo in the second listing, i.e.,multiple-let*
instead ofmultiple-value-let*
? – Offcolor