I have a class with a number of slots. I also have a builder function to make objects of that class such that passing the following list '(:id "john" :name "John Doe" :age 42)
to that function will construct a new object with those slots values. I will use that function to generate more than one object, using a list of lists.
How can I convert from a keyword like :id
to a slot name that SLOT-VALUE
can use?
Thanks.
If the keywords are the initargs for the class, then you just can call MAKE-INSTANCE
via APPLY
:
(defclass person ()
((id :initarg :id )
(name :initarg :name)
(age :initarg :age )))
CL-USER > (mapcar
(lambda (initargs)
(apply #'make-instance 'person initargs))
'((:id "john" :name "John Doe" :age 42)
(:id "mary" :name "Mary Doe" :age 42)))
(#<PERSON 402027AB7B> #<PERSON 402027AC33>)
The find-symbol
and symbol-name
functions will be helpful to you. If defclass
and slot-value
happen in the same package, you can use those functions as follows:
(defclass person ()
((id :initarg :id)
(name :initarg :name)
(age :initarg :age)))
(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42)
(find-symbol (symbol-name :id)))
If defclass
and slot-value
happen in two different packages, you need to give find-symbol
the name of the package where defclass
happens:
(in-package #:common-lisp-user)
(defpackage #:foo
(:use #:common-lisp)
(:export #:person))
(defpackage #:bar
(:use #:common-lisp #:foo))
(in-package #:foo)
(defclass person ()
((id :initarg :id)
(name :initarg :name)
(age :initarg :age)))
(in-package #:bar)
(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42)
(find-symbol (symbol-name :id) 'foo))
(find-symbol name &optional (package (sane-package)))
Function: Return the symbol named STRING in PACKAGE. If such a symbol is found then the second value is :INTERNAL, :EXTERNAL or :INHERITED to indicate how the symbol is accessible. If no symbol is found then both values are NIL.
(symbol-name symbol)
Function: Return SYMBOL's name as a string.
I realize that this is quite old, but I think that the most important point to be made here is:
Don't use slot-value
like that!
In order to get an accessor, use the :accessor
or :reader
slot options, and for passing values to the constructor, use :initarg
:
(defclass foo ()
((bar :accessor foo-bar :initarg :bar)))
This means: create a getter method and a setf expander named foo-bar
, and use a keyword argument named :bar
to make-instance
to initialize this slot's value.
Now you can instantiate such an object like this:
(make-instance 'foo :bar "quux")
or, if you get a property list of initargs (as Rainer had already shown):
(let ((initargs (list :bar "quux"))) ; getting this from somewhere
(apply #'make-instance 'foo initargs))
You can then get the value like this:
(foo-bar some-foo)
And set it with setf
as usual:
(setf (foo-bar some-foo) "wobble")
If you use :reader
instead of :accessor
, setting is not allowed. This is often useful to communicate intent of immutability.
Slot-value
is really for special situations in the lifetime of an object, such as when playing around with methods for initialize-instance
. That is an advanced topic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With