Here you have something to play with until your book arrives:
J.Owlsteam wrote:I wasn't thinking to do that at instatiation time, but inside the structure, at definition time, something like:
Code: Select all
(defstruct my-class
(attr1 0)
(attr2 (+ attr1 1)))
Common Lisp classes are stupid data containers like structures, the behaviour of objects is tied to the functions using the objects, not to the classes or objects themselves. This means that even with CLOS, the slot initialisation always happens at instatiation time, but I will show an example how to achieve the desired behaviour with Common Lisp and CLOS.
The CLOS class definition would look like this:
Code: Select all
(defclass my-class ()
((attr1 :initarg :attr1 :initform 0 :accessor my-class-attr1)
(attr2 :initform 1 :accessor my-class-attr2))
(:documentation "MY-CLASS does something."))
DEFCLASS automatically creates methods for the
MAKE-INSTANCE and
INITIALIZE-INSTANCE functions, that therefore are called a "generic functions" because they can have methods (in contrast to to an ordinary function defined by
DEFUN, that can have no methods). See
DEFGENERIC how to define your own generic functions.
:initarg :attr1 means that an instance of MY-CLASS is created by:
Code: Select all
(make-instance 'my-class :attr1 <value>)
:initform 0 means that if
MAKE-INSTANCE is called without the
:attr1 keyword argument, the default value for the ATTR1 slot in the new instance shall be 0 (zero).
:accessor my-class-attr1 means that a MY-CLASS-ATTR1 function is automatically created to get read/write access to the ATTR1 slot (like the slot accessor of a structure). In CLOS classes there also can be
:reader or
:writer functions for read-only or write-only access.
Initializing the ATTR2 Slot
The ATTR2 slot has no
:initarg option because its value shall be computed automatically by an :AFTER method of the
INITIALIZE-INSTANCE generic function, that you must define yourself. The method definition looks like this:
Code: Select all
(defmethod initialize-instance :after ((obj my-class) &key)
(setf (my-class-attr2 obj) (+ (my-class-attr1 obj) 1)))
:after means that this method shall be called after
INITIALIZE-INSTANCE is finished with calling all other methods.
In
(obj my-class) the
obj is the argument variable like in (defun foo (
obj) ...), and
my-class means that this method only shall be called if
obj is an object of class
my-class. In Common Lisp this is called "the OBJ argument is specialized on the MY-CLASS class".
The strange-looking
&key at the end of the argument list is necessary because the :AFTER method gets called with all arguments given to the original
MAKE-INSTANCE call:
Code: Select all
(make-instance 'my-class :attr1 <value>)
This means that
:attr1 <value> (if specified) is ignored in the :AFTER method.
The :AFTER method is called after
INITIALIZE-INSTANCE is finished with initializing a newly created instance. This means that in contrast to the
DEFSTRUCT examples above, at this point the new instance already exists, so we have access to all slots of the new instance. The ATTR1 slot is already initialized, first by the
:initform <value> argument in the
DEFCLASS definition, then optionally by an
:attr1 <value> argument to
INITIALIZE-INSTANCE that overwrites the
:initform <value> from the
DEFCLASS definition.
Now that a new instance is created and the ATTR1 slot in the new instance is initialized, the value for the ATTR2 slot can be computed from the value of the initialized ATTR1 slot without producing an error. The accessor functions work exactly like you already know from
DEFSTRUCT, the new instance can be referenced by the
obj argument variable of the :AFTER method:
Code: Select all
(setf (my-class-attr2 obj) (+ (my-class-attr1 obj) 1))
Writing a Constructor Function
In contrast to
DEFSTRUCT, a
DEFCLASS definition does not automatically generate a constructor function because there are too many possibilities how classes can be used. So if you don't want to write the full
MAKE-INSTANCE call including all keyword arguments every time anew you can write a constructor function like this:
Code: Select all
(defun make-my-object (attr1)
(make-instance 'my-class :attr1 attr1))
It'a good idea to add some code to the constructor function to make sure that the ATTR1 argument has a correct value before a new instance is created.
Now if you call the MAKE-MY-OBJECT constructor function:
Code: Select all
(make-my-object 123) => #<MY-CLASS {1005E36E93}>
Hmm, this is not very informative, but you can use
DESCRIBE to see that really works:
Code: Select all
CL-USER> (describe (make-my-object 123))
#<MY-CLASS {1005E03533}>
[standard-object]
Slots with :INSTANCE allocation:
ATTR1 = 123
ATTR2 = 124
Yeah!
- edgar