Re: General CL question: get-setf-expansion
Martin Simmons <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
Yes, get-setf-expansion solves the problems of order of subform evaluation and preventing multiple-evaluation. Anything else you want to do with reading and writing the underlying place is up to you to document (just like rotatef does, for example). Your assign macro is essentially what setf is doing. -- Martin Simmons LispWorks Ltd http://www.lispworks.com/ >>>>> On Tue, 30 Sep 2025 10:22:25 +0100, Tim Bradshaw (as tfb at cley dot com) said: > > I accidentally replied only to phoe about this yesterday because I can't drive phones. > > I think I've convinced myself that get-setf-expansion will always do the right thing. The reason isn't just because it allows you to write something equivalent to setf if you want to without having to special-case a lot of things (below), but because it offers some useful guarantees which enable you to write things which are related to setf but more complicated. > > In particular the spec says this: > > > The value returned by the accessing form is affected by execution of the storing form, but either of these forms might be evaluated any number of times. > > So this means that you can, without enormous pain, write code which needs to do just that: get-setf-expansion tells you how to deal with the multiple-evaluation problem. > > That's exactly what my defaulting macro needs to do, of course: evaluate the accessor form to check if the value(s) it returns are the default(s), and if they are evaluate the storing form to change them and then evaluate the accessor again to return the new values: > > > (defvar *h* (make-hash-table)) > *h* > > > > (defaulting (gethash 'a *h*) 4 :nth-value 1) > 4 > t > > --tim > > The thing I think is probably close to setf is: > > (defmacro assign (&rest pairs &environment e) > ;; This should be SETF > `(progn > ,@(collecting > (for ((tail (on-list pairs :by #'cddr))) > (destructuring-bind (place-form value-form . _) tail > (declare (ignore _)) > (multiple-value-bind (vars vals store-vars writer-form reader-form) > (get-setf-expansion place-form e) > (declare (ignore reader-form)) > (collect > `(let* ,(mapcar #'list vars vals) > (multiple-value-bind ,store-vars ,value-form > ,writer-form))))))))) > > > > On 29 Sep 2025, at 18:11, Michał phoe Herda (as phoe at disroot dot org) <[email protected]> wrote: > > > > W dniu 2025-09-29 18:58, Tim Bradshaw (as tfb at cley dot com) napisał(a): > >> The question is: does anything usable as a place to setf have to have a setf expansion, or is it allowed for things just to fall back to calling some presumed (setf x) function? I don't think it should be because if it was it would make writing a macro like this much harder, but I can't work out from the spec. Both LW and SBCL are empirically fine. > >> > > CLHS 5.1.2 should give you an answer. Basically, if there are no macros, no symbol macros, no SETF expansions, and no other special cases listed in that chapter, then the "compiler fallback" for (SETF (FOO 1 2 3 ...) BAR) is always (FUNCALL #'(SETF FOO) BAR 1 2 3 ...). > > This also tells you that, no, a place does not need to have a SETF expansion to be SETFable; if it does not match any of the eight first cases listed in CLHS 5.1.2, then it must have a proper SETF function (and therefore fall under the last, ninth case). > > https://www.lispworks.com/documentation/HyperSpec/Body/05_ab.htm > > > _______________________________________________ > Lisp Hug - the mailing list for LispWorks users > [email protected] > http://www.lispworks.com/support/lisp-hug.html > _______________________________________________ Lisp Hug - the mailing list for LispWorks users [email protected] http://www.lispworks.com/support/lisp-hug.html