Search code examples
lispcommon-lispletmultiple-value

How to format a nested multiple-value-bind the let way?


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))

?


Solution

  • 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))))