How to use funcall on a method to set an attribute of an object
common-lisp, oop
Solution
`SETF` is a special form (see http://www.lispworks.com/documentation/HyperSpec/Body/05_aa.htm for the part of the spec explaining it). Your second example works because the lisp implementation interprets `(test *test*)` syntactically.
To see what's going on, look at this session:
This is SBCL 1.0.56.0.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (defclass test () ((test :initform nil :accessor test)))
#<STANDARD-CLASS TEST>
* (defvar *test* (make-instance 'test))
*TEST*
* (macroexpand '(setf (test *test*) 123))
(LET* ((#:*TEST*606 *TEST*))
(MULTIPLE-VALUE-BIND (#:NEW605)
123
(FUNCALL #'(SETF TEST) #:NEW605 #:*TEST*606)))
T
* #'(setf test)
#<STANDARD-GENERIC-FUNCTION (SETF TEST) (1)>
* (macroexpand '(setf (funcall #'test *test*) 123))
(LET* ((#:G609 #'TEST) (#:*TEST*608 *TEST*))
(MULTIPLE-VALUE-BIND (#:NEW607)
123
(FUNCALL #'(SETF FUNCALL) #:NEW607 #:G609 #:*TEST*608)))
T
Note that the first macroexpansion grabs `#'(setf test)`, which is the writer function that gets automatically defined by your `defclass` call. The second blindly translates to `#'(setf funcall)`, which doesn't exist (hence the error).
To answer your "how can I work around it?" question, we'd probably need to know more about what you're trying to do. For example, you could use something like `(setf (slot-value object slot-name))` which would allow you to choose the slot programmatically.
Problem
Considering this code: ``` (defclass test () ((test :initform nil :accessor test))) #<STANDARD-CLASS TEST> (defvar *test* (make-instance 'test)) *TEST* ``` and this test: ``` (funcall #'test *test*) nil ``` one would expect that this works: ``` (setf (funcall #'test *test*) 123) ``` the same as ``` (setf (test *test*) 123) 123 ``` but it results in this: ``` ; in: LAMBDA NIL ; (FUNCALL #'(SETF FUNCALL) #:NEW1175 #:TMP1177 #:TMP1176) ; ==> ; (SB-C::%FUNCALL #'(SETF FUNCALL) #:NEW1175 #:TMP1177 #:TMP1176) ; ; caught WARNING: ; The function (SETF FUNCALL) is undefined, and its name is reserved by ANSI CL ; so that even if it were defined later, the code doing so would not be portable. ; ; compilation unit finished ; Undefined function: ; (SETF FUNCALL) ; caught 1 WARNING condition ``` Why doesn't it work, and how can I work around it? I tested it using either SBCL and CLISP with the same result.