ruricolist / serapeum

Utilities beyond Alexandria
MIT License
415 stars 41 forks source link

destructuring-into proposal #128

Closed sirherrbatka closed 1 year ago

sirherrbatka commented 2 years ago

I was thinking that it is slightly odd that such macro was not included in Alexandria, I write something to that effect pretty often.

(defmacro destructuring-into (lambda-list expression)
  (let ((gensyms (alexandria:make-gensym-list (length lambda-list))))
    `(destructuring-bind ,gensyms ,expression
       ,@(mapcar (lambda (target gensym) `(setf ,target ,gensym))
                 lambda-list
                 gensyms))))

What is your opinion? (note that the pasted code is wrong)

ruricolist commented 2 years ago

For just a list of places, I don't see a big advantage here over (setf (values ....) (values-list ...)). Although it is stricter.

It might be more interesting to do at least lambda-list destructuring, so one could write (destructuring-into (&key x y z) ...). This could use alexandria:parse-ordinary-lambda-list.

E.g.

(defmacro destructuring-into (lambda-list expr)
  (multiple-value-bind (req opt rest? kws aok? aux key?)
      (parse-ordinary-lambda-list lambda-list)
    (declare (ignore aok? key?))
    (let ((opt (mapcar #'car opt))
          (kws (mapcar #'cadar kws))
          (aux (mapcar #'car aux)))
      `(setf (values ,@req
                     ,@opt
                     ,@(and rest? (list rest?))
                     ,@kws
                     ,@aux)
             (apply (lambda ,lambda-list
                      (values ,@req
                              ,@opt
                              ,@(and rest? (list rest?))
                              ,@kws
                              ,@aux))
                    ,expr)))))

(let (x y z)
 (destructuring-into (&key x y z)
  (list :z 1 :y 2 :x 3))
  (list x y z))
=> 3 2 1
sirherrbatka commented 2 years ago

Well, I personally had in mind something that can be used like:

(let (x y z)
  (destructuring-into ((x) y z) '((1) 2 3))
  (list x y z))

=> 1 2 3

And that's won't be handled by values-list as far as i can tell.

ruricolist commented 2 years ago

It sounds like you want iterate:dsetq:

(let (x y z)
  (iterate:dsetq ((x) y z) '((1) 2 3))
  (list x y z))
=> (1 2 3)
sirherrbatka commented 2 years ago

Huh. It never occurred to me to even think about iterate outside ITER:ITERATE scope.

sirherrbatka commented 2 years ago

Having said that, yes, this is exactly what I would want. Thanks, I learned something new.