I have a simple task: Given a class, I need a function that can add elements to one of the fields of the class. It's easy to do if the function knows the name of the field, but what if the function doesn't know it and we need to pass it as an input parameter to the function. Let's say (defclass test1 () ((fld1 :accessor test1-fld1 :initarg :fld1 :type list)))
(setq o (make-instance 'test1 :fld1 nil)) (defun append-element (o el) (with-slots (fld1) o (setf fld1 (append fld1 (list el)))))
How to modify the append-element function so that it doesn't know about the fld1 and had semantics of (append-element o el field-name) so we can call it like (append-element o 'a 'fld1)?
> I have a simple task: > Given a class, I need a function that can add elements to one of the > fields of the class. > It's easy to do if the function knows the name of the field, but what > if the function doesn't know it and we need to pass it as an input > parameter to the function. > Let's say > (defclass test1 () > ((fld1 :accessor test1-fld1 :initarg :fld1 :type list)))
> (setq o (make-instance 'test1 :fld1 nil)) > (defun append-element (o el) > (with-slots (fld1) o > (setf fld1 (append fld1 (list el)))))
> How to modify the append-element function so that it doesn't know > about the fld1 and had semantics of (append-element o el field-name) > so we can call it like (append-element o 'a 'fld1)?
I am sure, it can be done as a macro (defmacro append-element (o el field) `(with-slots (,field) o (setf ,field (append ,field (list ,el)))))
But I'd like to know if it's possible to pass some kind of setf-able place to the function?
> (setq o (make-instance 'test1 :fld1 nil)) > (defun append-element (o el) > (with-slots (fld1) o > (setf fld1 (append fld1 (list el)))))
> How to modify the append-element function so that it doesn't know > about the fld1 and had semantics of (append-element o el field-name) > so we can call it like (append-element o 'a 'fld1)?
Not entirely sure I understand, but maybe the SLOT-VALUE accessor is what you're looking for?
> > (setq o (make-instance 'test1 :fld1 nil)) > > (defun append-element (o el) > > (with-slots (fld1) o > > (setf fld1 (append fld1 (list el)))))
> > How to modify the append-element function so that it doesn't know > > about the fld1 and had semantics of (append-element o el field-name) > > so we can call it like (append-element o 'a 'fld1)?
> Not entirely sure I understand, but maybe the SLOT-VALUE accessor is > what you're looking for?
> How to modify the append-element function so that it doesn't know > about the fld1 and had semantics of (append-element o el field-name) > so we can call it like (append-element o 'a 'fld1)?
You can use SLOT-VALUE if you are happy to set the slot directly. If you want to use the accessor (which you probably should want to do) then I think that you can use FDEFINITION, based on the accessor's name:
(defun set-field (o accessor new) (funcall (fdefinition `(setf ,accessor)) new o))
On Apr 30, 1:08 pm, Tim Bradshaw <t...@tfeb.org> wrote:
> On 2010-04-30 17:08:32 +0100, Trastabuga said:
> > How to modify the append-element function so that it doesn't know > > about the fld1 and had semantics of (append-element o el field-name) > > so we can call it like (append-element o 'a 'fld1)?
> You can use SLOT-VALUE if you are happy to set the slot directly. If > you want to use the accessor (which you probably should want to do) > then I think that you can use FDEFINITION, based on the accessor's name:
> (defun set-field (o accessor new) > (funcall (fdefinition `(setf ,accessor)) new o))
> I have a simple task: > Given a class, I need a function that can add elements to one of the > fields of the class. > It's easy to do if the function knows the name of the field, but what > if the function doesn't know it and we need to pass it as an input > parameter to the function. > Let's say > (defclass test1 () > ((fld1 :accessor test1-fld1 :initarg :fld1 :type list)))
> (setq o (make-instance 'test1 :fld1 nil)) > (defun append-element (o el) > (with-slots (fld1) o > (setf fld1 (append fld1 (list el)))))
> How to modify the append-element function so that it doesn't know > about the fld1 and had semantics of (append-element o el field-name) > so we can call it like (append-element o 'a 'fld1)?
Tim Bradshaw wrote: > On 2010-05-02 14:19:06 +0100, Norbert_Paul said: > This works, but it only works if you know the name FOO: you can't say > (let ((fun 'foo) #'(setf fun)) for instance: you need FDEFINITION for that.
I searched the HS a lot but couldn'd find any guarantee that an accessor foo always has an (fdefinition `(setf ,foo)), neither could I find the contrary.
On Apr 30, 12:08 pm, Trastabuga <lisper...@gmail.com> wrote:
> Given a class, I need a function that can add elements to one of the > fields of the class. > It's easy to do if the function knows the name of the field, but what > if the function doesn't know it and we need to pass it as an input > parameter to the function. > Let's say > (defclass test1 () > ((fld1 :accessor test1-fld1 :initarg :fld1 :type list))) > (setq o (make-instance 'test1 :fld1 nil)) > (defun append-element (o el) > (with-slots (fld1) o > (setf fld1 (append fld1 (list el)))))
If I wanted to stick with a function-based (as opposed to macro-based) solution, I would do it like this:
You can build more convenient functions on top of this one, like
(append-element-to-slot (object element slot) (append-element object element (lambda (o) (slot-value o slot)) (lambda (v o) (setf (slot-value o slot))))
(append-element-to-table-value (table element key) (append-element table element (lambda (ht) (gethash key ht)) (lambda (v ht) (setf (gethash key ht) v)))
I doubt it will ever be as syntactically clean as a macro-based solution, but it's hard to beat its ease of implementation.
> I searched the HS a lot but couldn'd find any guarantee that > an accessor foo always has an (fdefinition `(setf ,foo)), > neither could I find the contrary.
Indeed not. It's not because you can write (setf (foo x) v) that you can recover a (function (setf foo)) or a (fdefinition '(setf foo)).
SETF can implement the "standard" place in its own way, without having a (setf foo) function defined.
The correct way to "reify" a random place in general, is to wrap it in a closure, or a pair of closures.
See for example: http://groups.google.com/group/comp.lang.lisp/msg/1799d5db9267c523 which is wrong, since it doesn't use get-setf-expansion to avoid duplicate evaluation of the arguments to the place, but gives you the closure side (it answered on a question on variables, not places).
So a full solution would be:
;;; Locatives with multiple-value places.
(defmacro & (place &environment env) (multiple-value-bind (vars vals store-vars writer-form reader-form) (get-setf-expansion place env) `(let* (,@(mapcar (function list) vars vals) ,@store-vars) (lambda (m &rest values) (ecase m ((set) (psetf ,@(loop :for v :in store-vars :nconc (list v '(pop values)))) ,writer-form) ((get) ,reader-form))))))
(defun deref (locative) (funcall locative 'get))
;; Notice that using (defun (setf deref) ...) would prevent to store ;; multiple values, if the place accepted them. So we must use ;; defsetf with a macro setter.
> ;; Notice that using (defun (setf deref) ...) would prevent to store ;; > multiple values, if the place accepted them. So we must use ;; defsetf > with a macro setter.
> I searched the HS a lot but couldn'd find any guarantee that > an accessor foo always has an (fdefinition `(setf ,foo)), > neither could I find the contrary.
It pretty much has to work for ordinary CLOS classes (by "ordinary" I mean "assuming no one has done too much MOP stuff"): If I can define a method on (SETF FOO) then I can use FDEFINITION on it.