common-lispsetterclossetf

How to execute a function on setf place


I have a list which contains some symbols and values. The goal is to setf the class slot with the accessor, whose symbol is provided by the list :

(defclass my-class ()
 ((attr :accessor attr)))

(let ((to-call '(attr "some-value"))
      (obj (make-instance 'my-class)))
 (setf `(,(car to-call) obj) (cadr to-call)))

I have tried via a macro :

(defmacro call-accessor (to-call)
 `(setf (,(car to-call) obj) "some-value"))

(let ((to-call '(attr "some-value"))
      (obj (make-instance 'my-class)))
 (call-accessor to-call))

Which fails too, since to-call is a symbol and not a list.

How can I setf a slot via an accessor corresponding to a symbol in my list ?

Thank you.


Solution

  • Calling the accessor function

    The goal is to setf the class slot with the accessor

    An accessor is a pair of functions. You can get the part which sets the value via FDEFINITION. The name of the function is a list (SETF accessor-name ). This is unusual: Common Lisp has in this case function names which are not symbols, but lists.

    CL-USER 14 > (let ((to-call '(attr "some-value"))
                       (obj (make-instance 'my-class)))
                   (funcall (fdefinition `(setf ,(first to-call)))
                            (second to-call)
                            obj)
                   (describe obj))
    
    #<MY-CLASS 40200614FB> is a MY-CLASS
    ATTR      "some-value"
    

    Using a function call-accessor:

    CL-USER 25 > (let ((to-call '(attr "some-value"))
                       (obj (make-instance 'my-class)))
                   (flet ((call-accessor (obj to-call)
                            (funcall (fdefinition `(setf ,(first to-call)))
                                     (second to-call)
                                     obj)))
                     (call-accessor obj to-call)
                     (describe obj)))
    
    #<MY-CLASS 402000220B> is a MY-CLASS
    ATTR      "some-value"
    

    using SETF with APPLY to hide the FDEFINITION call

    To use setf with a computed accessor, one might need to use an apply form and a custom function.

    Something like call-accessor would naturally be a function, because it does runtime lookup and takes values. Trying to use a macro would be more useful if the accessor would be known at compile time.

    CL-USER 23 > (let ((to-call '(attr "some-value"))
                       (obj (make-instance 'my-class)))
                   (flet (((setf my-setter) (new-value object accessor) 
                            (funcall (fdefinition `(setf ,accessor))
                                     new-value
                                     obj)))
                     (flet ((call-accessor (obj to-call)
                              (setf (apply #'my-setter obj (list (first to-call)))
                                    (second to-call))))
                       (call-accessor obj to-call)
                       (describe obj))))
    
    #<MY-CLASS 40200009AB> is a MY-CLASS
    ATTR      "some-value"
    

    Choice of data structure

    I think it's okay to compute accessor functions and similar. There may be use cases for that. CLOS was designed to be dynamic and reflective to allow these things.