parameterscommon-lispsbcl

Is there a way to use destructuring bind with &rest?


I have a function that uses &rest which returns a list of the varying amount of args to the function.

I often break lists up with destructuring bind (db). However, db is useful for when the number of variables are known.

What is the best way to break up a &rest list where the number of values vary most of the time?


Solution

  • If you don't know the number of things you wish to bind you can't possibly solve this problem because the number of variables is unknown until runtime.

    So the only possible case you can solve is where the number of elements in the list is constrained in some way: in particular it needs to have a maximum number of 'interesting' elements that you want to bind variables to at one time. In that case you can either use a matching macro of some kind, or if your requirements are simpler just use destructuring-bind:

    (defun foo (&rest args)
      (destructuring-bind (&optional (a nil ap) (b nil bp) (c nil cp) &rest extras) args
        (unless (null extras)
          (error "too many args"))
        ... use AP, BP, CP to know if one, two or three arguments were
        ... provided, and A, B, C to know what they were.
        ...))
    

    More generally you might want a matching macro. Another answer has already mentioned Trivia, but destructuring-match provides a case-like variant of destructuring-bind:

    (defun foo (&rest args)
      (destructuring-match args
        ((a &optional (b 0))
         (+ a b))
        ((a b c)
         (+ a (- b c)))
        (otherwise
         (error "need between 1 and 3 arguments"))))
    

    However the above version of foo can actually be happily be dealt with by just destructuring-bind:

    (defun foo (&rest args)
      (destructuring-bind (a &optional (b 0) (c 0)) args
        (+ a (- b c))))
    

    The only difference here is that you don't get to control the error case. destructuring-match is really intended for pulling apart much more complex structures in macros.