Could any CL'er please explain 'slots' in CLOS? I am finding it difficult to understand the part after the slot name. That is in :
(defclass foo ()
(data1 :initarg foo))
What do the 'initarg' and other such similar things mean? I am re-reading manuals. So, I would really appreciate if any of you here could explain it to a layman like me.
Thanks!
Your example is slightly wrong. It has to be:
(defclass foo ()
((data1 :initarg foo)))
Notice the added parentheses to indicate a list of slot descriptions.
DEFCLASS takes a list of slots. So with two slots we have:
(defclass foo ()
((data1 :initarg :data1arg
:initform (random 1.0)
:type number
:documentation "doc here"
:accessor foo-data1-acc)
(data2 :initarg :data2arg)))
DATA1 is the name of the slot. Behind that you find pairs of :keyword value.
:INITARG tells you what the parameter for MAKE-INSTANCE is.
(make-instance 'foo :data1arg 10) ; creates the object and sets the slot data1 to 10.
Usually you should use a keyword symbol (like :data1arg here).
:INITFORM sets the slot by default, when the object is created. Like in:
(make-instance 'foo) ; creates the object. The slot is set to the value of the initform.
:TYPE specifies the type of the slot's object.
:DOCUMENTATION is just a string for, well, documentation.
:ACCESSOR specifies a function to read and write the slot.
(foo-data1-acc some-foo-object-here) ; read
(setf (foo-data1-acc some-foo-object-here) 7) ; write
Note that you can write the pairs in any order and that you can also specify multiple accessor functions. There are also :READER and :WRITER functions.
With CLOS you can specify all that within the DEFCLASS macro. These things are not automatically generated like in defstruct, which has a shorter notation.
The description of DEFCLASS is here: DEFCLASS.
Short CLOS Intro.
In a slot specification, the general syntax is (slot-name [slot-option option-value]...). The essentially-authoritatiev reference is the HyperSpec page on defclass, but in short:
:reader A function to read the value of the slot
:writer A function to write the value of the slot
:accessor A function to both read and (via SETF) set the value of the slot
:initarg A symbol to set the slot's value from MAKE-INSTANCE
There are more, but that list is the four I mostly use (actually, I mostly use :initarg and one of :accessor or :reader).
Related
Common Lisp is lexically scoped, but there is a possibility to create dynamic bindings with (declare (special *var*)). What I need, is a way to create a dynamically scoped structure slot, whose value is visible to all other slots. Here is an example:
(defun start-thread ()
*delay*) ;; We defer the binding of *delay*
This works for a usual lexical environment:
(let ((*delay* 1))
(declare (special *delay*))
(start-thread)) ;; returns 1
This does not work:
(defstruct table
(*delay* 0)
(thread (start-thread)))
(make-table) ;; => Error: *delay* is unbound.
My questions are
How to refer to the slot delay from other slots?
How to make the slot delay dynamically scoped, so that its value becomes visible
for the function (start-thread) ?
The first thing to realise that there's no good way to have a dynamically-scoped slot in an object (unless the implementation has some deep magic to support this): the only approach that will work is to use, essentially, explicit shallow-binding. Something like this macro, for instance (this has no error checking at all: I just typed it in):
(defmacro with-horrible-shallow-bound-slots ((&rest slotds) object &body forms)
(let ((ovar (make-symbol "OBJECT"))
(slot-vars (mapcar (lambda (slotd)
(make-symbol (symbol-name (first slotd))))
slotds)))
`(let ((,ovar ,object))
(let ,(mapcar (lambda (v slotd)
`(,v (,(first slotd) ,ovar)))
slot-vars slotds)
(unwind-protect
(progn
(setf ,#(mapcan (lambda (slotd)
`((,(first slotd) ,ovar) ,(second slotd)))
slotds))
,#forms)
(setf ,#(mapcan (lambda (slotd slot-var)
`((,(first slotd) ,ovar) ,slot-var))
slotds slot-vars)))))))
And now if we have some structure:
(defstruct foo
(x 0))
Then
(with-horrible-shallow-bound-slots ((foo-x 1)) foo
(print (foo-x foo)))
expands to
(let ((#:object foo))
(let ((#:foo-x (foo-x #:object)))
(unwind-protect
(progn (setf (foo-x #:object) 1) (print (foo-x foo)))
(setf (foo-x #:object) #:foo-x))))
where all the gensyms with the same name are in fact the same. And so:
> (let ((foo (make-foo)))
(with-horrible-shallow-bound-slots ((foo-x 1)) foo
(print (foo-x foo)))
(print (foo-x foo))
(values))
1
0
But this is a terrible approach because shallow binding is terrible in the presence of multiple threads: any other thread that wants to look at foo's slots will also see the temporary value. So this is just horrid.
A good approach is then to realise that while you can't safely dynamically-bind a slot in an object, you can dynamically bind a value which that slot indexes by using a secret special variable to hold a stack of bindings. In this approach the values of slots do not change, but the values they index do, and can do so safely in the presence of multiple threads.
A way of doing this this is Tim Bradshaw's fluids toy. The way this works is that you define the value of a slot to be a fluid, and then you can bind that fluid's value, which binding has dynamic scope.
(defstruct foo
(slot (make-fluid)))
(defun outer (v)
(let ((it (make-foo)))
(setf (fluid-value (foo-slot it) t) v) ;set global value
(values (fluid-let (((foo-slot it) (1+ (fluid-value (foo-slot it)))))
(inner it))
(fluid-value (foo-slot it)))))
(defun inner (thing)
(fluid-value (foo-slot thing)))
This often works better with CLOS objects because of the additional flexibility in things like naming and what you expose (you almost never want to be able to assign to a slot whose value is a fluid, for instance: you want to assign the value of the fluid).
The system uses a special variable behind the scenes to implement deep binding for fluids, so will work properly with threads (ie distinct threads can have different bindings for a fluid) assuming the implementation treats special variables sensibly (which I'm sure all multithreaded implementations do).
I don't think that this makes sense. Variables have scope and extent, but values just are, and slots are just parts of values. Additionally, threads do not inherit dynamic bindings.
If you want to have some kind of object that is dynamically changed (so to speak), you need to put it into a dynamic variable as a whole value, and do re-bindings with modified versions (preferably on the basis of some immutability, i. e. persistent datastructures, e. g. with FSet).
I'm doing a bit of guessing about what you need here, but I think using a class and initialize-instance will give you what you want. In the code below, I rewrote your struct as a class, and the object itself is passed to initialize-instance in a call to (make-instance 'table).
(defclass table ()
((delay :initform 5)
(thread)))
(defun start-my-thread (obj)
(print (slot-value obj 'delay)))
(defmethod initialize-instance :after ((obj table) &key)
(start-my-thread obj))
(make-instance 'table)
; above call will print 5
This asks about initializing slots from other slots. What I want to achieve instead is to take some arguments as input - perhaps but not necessarily to make-instance - and convert these arguments into the class slots for storage. In effect, I want to separate the implementation of the class from its (initialization) interface.
Is there a recommended way to achieve this?
The simplest way I can imagine is simply create a (defun make-my-object ...) as the interface. This may then call make-instance with appropriate arguments.
For example, imagine
(defclass my-object () (slot-1 slot-2))
(defun make-my-object (arg-1 arg-2)
(make-instance 'my-object
:slot-1 (+ arg-1 arg-2)
:slot-2 (- arg-1 arg-2)))
Other ways I can imagine include implementing an initialize-instance :after that takes arg-1 and arg-2 as keyword arguments and initializes slot-1 and slot-2 appropriately. However, since after methods are called in the least-specific-first-order, that means that superclass slots will be initialized before current-class slot. On the other hand, what looks more common is that one will take arguments for constructing current-class, and on the basis of these arguments, one will initialize the super-class slots.
The alternative is initialize-instance :before - or also :around - but if multiple classes in the hierarchy have such "interface-implementation" differences, I don't see this working, unless I can pass arguments to call-next-method.
Are there other ways?
EDIT: Thanks to #ignis volens for bringing to my notice that (one of) my main concern(s) is about superclass slots being initialized from subclass slots. Is there a recommended way to do this?
I am not sure I understand your question. The answer is almost certainly after methods on initialize-instance I think. You say that this will cause slots defined in superclasses to be initialized first: yes, it will, and that almost certainly what you want to happen. Slots defined in superclasses don't generally depend for their values on subclass slots (its always possible to think of exceptions to everything) and so initialising in least-specific first order is almost always what you want.
The two common ways of initializing slots that I use are either to simply declare what their initargs are in the definition:
(defclass minibeast ()
((legs :initform 'uncountable
:initarg :legs
:initarg :leg-count
:accessor legs)
(tentacles :initform 'many
:initarg :tentacles
:initarg :number-of-tentacles
:accessor tentacles)))
And now (make-instance 'minibeast :legs 87) does what you expect. And this works (because, obviously it has to if the two slots were defined in different classes):
(defclass awful-monster ()
((legs :initform 'uncountable
:initarg :legs
:initarg :leg-count
:accessor legs)
(appendages :initform 'many
:initarg :legs
:initarg :appendages)))
Now (make-instance 'awful-monster :legs 93) will result in an awful monster with 93 legs and 93 appendages.
However that method perhaps doesn't qualify as separating interface from implementation. You may also want to perform some computation when initialising slots. In both these cases after methods on initialize-instance are generally the right approach:
(defclass horrible-monster ()
((legs :initform 983
:accessor legs)
(eyes :initform 63
:accessor eyes)
(appendages
:reader appendages)))
(defmethod initialize-instance :after
((m horrible-monster) &key eyes legs (stalky-eyes t))
(with-slots ((e eyes) (l legs) appendages) m
(when eyes (setf e eyes))
(when legs (setf l legs))
(setf appendages (if stalky-eyes (+ e l) l))))
And now horrible monsters will get the appropriate number of appendages (I am not sure why horrible monsters don't know whether their eyes are on stalks or not: perhaps they don't have mirrors).
There are any number of other combinations of course. You might well not want to have user code call make-instance explicitly but wrap things up in some function:
(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
&allow-other-keys)
(let ((the-remaining-args (copy-list args)))
;; No doubt alexandria or something has a way of doing this
(remf the-remaining-args ':sort-of-horrible0thing)
(apply #'make-instance sort-of-horrible-thing the-remaining-args)))
And now, of course you can have some bespoke initialzation protocol easily:
(defgeneric enliven-horrible-thing (horrible-thing &key)
(:method :around ((horrible-thing t) &key)
(call-next-method)
t))
(defun make-awful-thing (&rest args &key (sort-of-horrible-thing 'horrible-monster)
&allow-other-keys)
(let ((the-remaining-args (copy-list args)))
;; No doubt alexandria or something has a way of doing this
(remf the-remaining-args ':sort-of-horrible0thing)
(apply #'enliven-horrible-thing
(apply #'make-instance sort-of-horrible-thing
the-remaining-args)
the-remaining-args)))
(defmethod enliven-horrible-thing ((horrible-thing horrible-monster)
&key (ichor t) (smell 'unspeakable))
...)
I am reading the book Object Oriented Programming in Common Lisp from Sonja Keene.
In chapter 7, the author presents:
(class-name class-object)
This would make possible to query a class object for its name.
Using SBCL and the SLIME's REPL, I tried:
; SLIME 2.26.1
CL-USER> (defclass stack-overflow ()
((slot-1 :initform 1 )
(slot-2 :initform 2)))
#<STANDARD-CLASS COMMON-LISP-USER::STACK-OVERFLOW>
CL-USER> (make-instance 'stack-overflow)
#<STACK-OVERFLOW {1002D188E3}>
CL-USER> (defvar test-one (make-instance 'stack-overflow))
TEST-ONE
CL-USER> (slot-value test-one 'slot-1)
1
CL-USER> (class-name test-one)
; Evaluation aborted on #<SB-PCL::NO-APPLICABLE-METHOD-ERROR {10032322E3}>.
The code above returns the error message below:
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP:CLASS-NAME (1)>
when called with arguments
(#<STACK-OVERFLOW {1003037173}>).
[Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]
How would be the proper use of class-name?
Thanks.
The argument to class-name must be a class object, not an instance of the class.
Use class-of to get the class of the instance, then you can call class-name
(class-name (class-of test-one))
Using #Barmar's hint on a comment, this would the correct approach with class-name:
CL-USER> (class-name (defclass stack-overflow ()
((slot-1 :initform 1 )
(slot-2 :initform 2))))
STACK-OVERFLOW
class-name receives as an argument a class. In order to work with instances, the correct approach is using class-of:
CL-USER> (class-of 'test-one)
#<BUILT-IN-CLASS COMMON-LISP:SYMBOL>
I am not sure why class-name would be helpful, though.
I have recently encountered a confusing dichotomy regarding structures in Lisp.
When creating a structure with (defstruct), we specify the slots by keyword (:slotname). But when accessing it, we use local symbols ('slotname).
Why? This makes no sense to me.
Also, doesn't this pollute the keyword package every time you declare a structure?
If I try to access the slots by keyword, I get confusing errors like:
When attempting to read the slot's value (slot-value), the slot :BALANCE is
missing from the object #S(ACCOUNT :BALANCE 1000 :CUSTOMER-NAME "John Doe").
I don't understand this message. It seems to be telling me that something right under my nose doesn't exist.
I have tried declaring the structure using local symbols; and also with unbound keywords (#:balance) and these don't work.
DEFSTRUCT is designed in the language standard in this way:
slot-names are not exposed
there is no specified way to get a list of slot-names of a structure class
there is no specified way to access a slot via a slot-name
thus at runtime there might be no slot-names
access to slots is optimized with accessor functions: static structure layout, inlined accessor functions, ...
Also explicitly:
slot-names are not allowed to be duplicate under string=. Thus slots foo::a and bar::a in the same structure class are not allowed
the effects of redefining a structure is undefined
The goal of structures is to provide fast record-like objects without costly features like redefinition, multiple inheritance, etc.
Thus using SLOT-VALUE to access structure slots is an extension of implementations, not a part of the defined language. SLOT-VALUE was introduced when CLOS was added to Common Lisp. Several implementations provide a way to access a structure slot via SLOT-VALUE. This then also requires that the implementation has kept track of slot names of that structure.
SLOT-VALUE is simply a newer API function, coming from CLOS for CLOS. Structures are an older feature, which was defined already in the first version of Common Lisp defined by the book CLtL1.
You used make-instance to create a class instance and then you are showing a struct, I am confused.
structs automatically build their accessor functions. You create it with make-account. Then you'd use account-balance instead of slot-value.
I don't know what is the expected behavior to use make-instance with a struct. While it seemed to work on my SBCL, you are not using structs right.
(defstruct account
(balance))
(make-account :balance 100)
#S(ACCOUNT :BALANCE 100)
(account-balance *)
100
With classes, you are free to name your accessor functions as you want.
;;(pseudocode)
(defclass bank-account ()
((balance :initform nil ;; otherwise it's unbound
:initarg :balance ;; to use with make-instance :balance
:accessor balance ;; or account-balance, as you wish.
)))
(make-instance 'bank-account :balance 200)
#<BANK-ACCOUNT {1009302A33}>
(balance *)
200
https://lispcookbook.github.io/cl-cookbook/data-structures.html#structures
http://www.lispworks.com/documentation/HyperSpec/Body/m_defstr.htm
the slot :BALANCE is missing from the object #S(ACCOUNT :BALANCE 1000 :CUSTOMER-NAME "John Doe").
The slot name is actually balance and the representation uses the generated initargs. With the class object, the error message might be less confusing:
When attempting to read the slot's value (slot-value), the slot :BALANCE is missing from the object #<BANK-ACCOUNT {1009302A33}>.
First of all, see Rainer's excellent answer on structures. In summary:
Objects defined with defstruct have named accessor functions, not named slots. Further the field names of these objects which are mentioned in the defstruct form must be distinct as strings, and so keywords are completely appropriate for use in constructor functions. Any use of slot-value on such objects is implementation-dependent, and indeed whether or not named slots exist at all is entirely implementation-dependent.
You generally want keyword arguments for the constructors for the reasons you want keyword arguments elsewhere: you don't want to have to painfully provide 49 optional arguments so you can specify the 50th. So it's reasonable that the default thing defstruct does is that. But you can completely override this if you want to, using a BOA constructor, which defstruct allows you to do. You can even have no constructor at all! As an example here is a rather perverse structure constructor: it does use keyword arguments, but not the ones you might naively expect.
(defstruct (foo
(:constructor
make-foo (&key ((:y x) 1) ((:x y) 2))))
y
x)
So the real question revolves around classes defined with defclass, which usually do have named slots and where slot-value does work.
So in this case there are really two parts to the annswer.
Firstly, as before, keyword arguments are really useful for constructors because no-one wants to have to remember 932 optional argument defaults. But defclass provides complete control over the mapping between keyword arguments and the slots they initialise, or whether they initialise slots at all or instead are passed to some initialize-instance method. You can just do anything you want here.
Secondly, you really want slot names for objects of classes defined with defclass to be symbols which live in packages. You definitely do not want this to happen:
(in-package "MY-PACKAGE")
(use-package "SOMEONE-ELSES-PACKAGE")
(defclass my-class (someone-elses-class)
((internal-implementation-slot ...)))
only to discover that you have just modified the definition of the someone-elses-package::internal-implementation-slot slot in someone-elses-class. That would be bad. So slot names are symbols which live in packages and the normal namespace control around packages works for them too: my-package::internal-implementation-slot and someone-elses-package::internal-implementation-slot are not (usually) the same thing.
Additionally, the whole keyword-symbol-argument / non-keyword-symbol-variable thing is, well, let's just say well-established:
(defun foo (&key (x 1))
... x ...)
Finally note, of course, that keyword arguments don't actually have to be keywords: it's generally convenient that they are because you need quotes otherwise, but:
(defclass silly ()
((foo :initarg foo
:accessor silly-foo)
(bar :initarg bar
:accessor silly-bar)))
And now
> (silly-foo (make-instance 'silly 'bar 3 'foo 9))
9
Suppose I have a class called board:
(defclass board ()
((blocker :accessor blocker :initarg :blocker :initform 0))
According to this book I can define a custom setf for blocker by:
(defmethod (setf blocker) (new-blocker (b board))
(setf (slot-value b 'blocker) new-blocker))
However, steel bank common lisp will say function not defined, even though I have evaluated it. Does anyone know what's wrong here?
That looks correct. Note that you are redefining the already existing setf method that you created by specifying :accessor blocker. SBCL will give you a style-warning about that.
Your mistake is somewhere else. Are you in a different package, perhaps? Try to show the steps you have taken in your IDE to compile and load those forms, and to attempt to run that method invocation.
You have to declare a generic function before defining any methods.
(defgeneric (setf blocker) (new-blocker board))
See this chapter in Practical Common Lisp for an example.