Re: Auto-creation in a SETF method

JP Massar <[email protected]> Mon, 23 Feb 2004 23:20:16 -0800
Newsgroups gmane.lisp.clump
Message-ID <[email protected]>
At 12:25 PM 2/23/2004, Peter Seibel wrote:
>JP Massar <[email protected]> writes:
>
> > Suppose I've defined an class called ZOOP, and it has a slot SLOT.
> >
> >
> > Now suppose, for whatever perverted reason, I want the following:
> >
> > (let ((x nil))
> >    (setf (ZOOP-SLOT x) 23))
> >
> > to 'just work', that is, if X is not a ZOOP, I want to effectively have the
> > SETF generate
> >
> > (SETQ X (MAKE-INSTANCE :ZOOP))
>
>
>Unless I misunderstand your requirements here is--I think--an
>existance proof that it *is* possible. This also handles X being an
>arbitrary place and gives a semi-meaningful error message if X is not
>a place:
>
>   (defclass zoop () ((slot :initarg :slot :reader zoop-slot)))
>
>   (define-setf-expander zoop-slot (x &environment env)
>     (multiple-value-bind (xtemps xvals xstore-vars xwriter xreader)
>         (get-setf-expansion x env)
>       (let ((store (gensym))
>             (xstore (first xstore-vars)))
>         (if (rest xstore-vars) (error "To many store vars."))
>         (values
>          nil
>          nil
>          (list store)
>          `(let*
>               (,@(mapcar #'list xtemps xvals)
>                  (,xstore
>                   (if (typep ,xreader 'zoop) ,xreader (make-instance 
> 'zoop))))
>             (setf (slot-value ,xstore 'slot) ,store)
>             ,xwriter
>             ,store)
>          `(slot-value ,xreader 'slot)))))

Ah, wonderful!

Just incredible, super cool.

I doubt that I would have ever figured that out.  Thanks!!!

Here's my modification of your solution to work on my real problem:
not a slot accessor but in effect an array accessor.  It seems to do
the trick.

;;; If you understand this, you're a better Lisper than me.
;;; Thanks to Peter Seibel to providing the template.

(define-setf-expander gref (x &rest indices &environment env)
   (multiple-value-bind (xtemps xvals xstore-vars xwriter xreader)
       (get-setf-expansion x env)
     (let ((store (gensym))
           (xstore (first xstore-vars))
           (index-gensyms
            (loop for j from 0 for i in indices collect
                  (progn i (gensym (format nil "INDEX~D-" j))))))
       (if (rest xstore-vars) (error "To many store vars."))
       (values
        index-gensyms
        indices
        (list store)
        `(let*
             (,@(mapcar #'list xtemps xvals)
              (,xstore
               (if (ignore-errors (typep ,xreader 'garray))
                   ,xreader
                 (create-garray-from-&rest-indices ,@index-gensyms))))
           (setf-gref ,xstore ,store ,@index-gensyms)
           ,xwriter
           ,store)
        `(gref ,xreader ,@index-gensyms)))))