In this section, we explain how to create and use an instance.
• Creating instance: | ||
• Accessing instance: | ||
• Describing instance: | ||
• Changing classes: |
Using class object, you can create an instance of the class
by a generic function make
.
A specialized method for standard <class>
is defined:
Creates an instance of class and returns it. Arg … is typically a keyword-value list to initialize the instance.
Conceptually, the default make
method is defined
as follows:
(define-method make ((class <class>) . initargs) (let ((obj (allocate-instance class initargs))) (initialize obj initargs) obj))
That is, first it allocates memory for class’s instance,
then initialize it with the initialize
method.
Returns a newly-allocated uninitialized instance of class.
The default initialize method for <object>
works as follows:
Among the default slot allocation classes, only
instance-allocated slots are initializable and are handled by
the above sequence. Class-allocated slots (e.g. its
slot allocation is either :class
or :each-subclass
)
are initialized when the class object is created, if
:init-value
or :init-form
slot option is given.
Virtual slots aren’t initialized at all.
An user-defined allocation class can be configured either initializable or not initializable; see Metaobject protocol for the details.
If you specialize initialize
method, make sure to call
next-method
so that the slots are properly initialized
by the default sequence, before accessing any slot of the
newly created instance.
Typically you specialize initialize
method for
your class to customize how the instance is initialized.
It is not common to specialize allocate-instance
method.
However, knowing that how make
works, you can specialize
make
itself to avoid allocation of instance in
some circumstances (e.g. using pre-allocated instances).
Returns a value of the slot slot of object obj.
If the specified slot is not bound to any value, a generic function
slot-unbound
is called with three arguments, obj’s class,
obj, and slot. The default behavior of slot-unbound
is to signal an error.
If the object doesn’t have the specified slot,
a generic function slot-missing
is called with three
arguments, obj’s class,
obj, and slot. The default behavior of slot-missing
is to signal an error.
Alters the value of the slot slot of object obj to the value value.
If the object doesn’t have the specified slot, a generic function
slot-missing
is called with four arguments,
obj’s class, obj, slot, value.
Returns true if object obj’s slot slot is bound, otherwise returns false.
If the object doesn’t have the specified slot, a generic function
slot-missing
is called with three arguments,
obj’s class, obj, slot.
Returns true if obj has the slot named slot.
This function implements the common idiom. It can be defined like the following code (but it may be optimized in the future versions).
(define (slot-push! obj slot value) (slot-set! obj slot (cons value (slot-ref obj slot))))
Reverse operation of slot-push!
.
If the value of slot of obj
is a pair, removes its car and returns the removed item.
When the value of slot is not a pair, or the slot is unbound, fallback is returned if it is provided, otherwise an error is signaled.
These methods just calls slot-ref
and slot-set!
, respectively.
They are slightly less efficient than directly calling slot-ref
and slot-set!
, but more compact in the program code.
This generic function is called when an unbound slot value is retrieved. The return value of this generic function will be returned to the caller that tried to get the value.
The default method just signals an error.
This generic function is called when a non-existent slot value is retrieved or set. The return value of this generic function will be returned to the caller that tried to get the value.
The default method just signals an error.
Returns a class metaobject of obj. If obj’s class has been redefined, but obj is not updated for the change, then this procedure returns the original class of obj without updating obj.
You need this procedure in rare occasions, such as within
change-class
method, in which you don’t want to trigger
updating obj (which would cause infinite loop).
When slot’s :allocation
option is either :class
or :each-subclass
, these procedures allow you to
get/set the value of the slot without having an instance.
Generic function version of slot-ref
, slot-set!
and
slot-bound?
. Class must be the class of obj.
Besides being generic, these functions are different from their procedural versions that they don’t trigger class redefinition when obj’s class has been redefined (i.e. in which case, class should be the original class of obj).
Note: Unlike CLOS, slot-ref
etc. don’t call the generic
function version in it, so you can’t customize the behavior
of slot-ref
by specializing slot-ref-using-class
.
So the primary purpose of those generic functions are to be
used within change-class
method; especially, slot-ref
etc.
can’t be used during obj’s being redefined, since they
trigger class redefinition again (see Changing classes for details).
In REPL, you can invoke ,d
or ,describe
toplevel
command to view the detailed description of an object
(see Working in REPL). It is realized as a generic function
describe
:
Prints the detail information about a Scheme object obj. The default method shows obj’s class, and if it has any slots, the list of slot names and their values.
gosh> (describe (sys-stat "Makefile")) #<<sys-stat> 0x1e7de60> is an instance of class <sys-stat> slots: type : regular perm : 436 mode : 33204 ino : 3242280 dev : 2097 rdev : 0 nlink : 1 uid : 500 gid : 500 size : 19894 atime : 1435379061 mtime : 1432954340 ctime : 1432954340
When you load gauche.interactive
module (which is already done
when you’re in REPL), a bunch of methods for built-in objects
are defined to describe
, showing information specific
to their classes. See gauche.interactive
- Utilities for interactive session, for more details.
You can also define a specialized describe
method on your own class
for customized description. The common pattern of a customized
describe method is (1) call describe-common
first, which
prints the common opening line, and (2) at the end, make sure to
return zero values—which will play nicely with REPL.
You can print whatever you like inbetween.
(define-method describe ((obj <my-class>)) ;; common opening line, "#<...> is an instance of class <my-class>" (describe-common obj) ;; print relevant information ... custom description ... ;; if you have slots to show: (describe-slots obj) ;; return 0 values. This works nicely in REPL. (values))
This procedure prints the common opening line of describe
.
Custom describe
method should call this at the beginning.
(describe-common 1)
⇒ prints (a b c) is an instance of class <pair>
This shows the list of slots and their values of obj, as
in the default describe
method. Useful for the custom
describe method, when you want to print some extra information
and list of slot values.
(describe-slots (current-input-port))
⇒ prints
slots:
name : "(standard input)"
buffering : :full
sigpipe-sensitive?: #f
current-line: 7
current-column: 0
link : #<oport (standard output) 0x7ff0cc6cddd0>
By default, this procedure does not display the slots whose name begins
with %
. It reflects the convention that private or internal
slots are named so. There’re no mechanism in the Gauche object
system to “hide” such slots; it is purely for users’ convenience,
not to clutter describe
output with excess details.
If necessary, you can set the dynamic state describe-details
#t
to make describe-slots
show all the slots.
This is a global dynamic state to control whether describe
shows auxiliary details. When called without arguments, it
returns the current state. When given flag, which is a boolean,
it sets flag as the current state, and returns the previous state.
The defalut value is #f
, in which case describe
does not
show the details. Notably, an object’s slots whose name begins with
%
won’t be shown.
Currently, the state is global and changing it is not thread-safe. We intend this as a REPL user-interface parameter, rather than something manipulated by a program. We may change it to thread-safe, or even a parameter, in future if it is found useful.
An unique feature of CLOS-family object system is that you can change classes of an existing instance. The two classes doesn’t need to be related; you can change a sewing machine into an umbrella, if you like.
Changes an object obj’s class to new-class.
The default method just calls change-object-class
procedure.
Changes an object obj’s class from orig-class to new-class. This isn’t a generic function—changing object’s class needs some secret magic, and this procedure encapsulates it.
The precise steps of changing class are as follow:
allocate-instance
.
Note that initialize
method of new-class
isn’t called on obj.
If you desire, you can call it by your own change-class
method.
Change-object-class
returns obj.
Usually a user is not supposed to call change-object-class
directly.
Instead, she can define a specialized change-class
. For example,
if she wants to carry over the slot x
of old class to the
slot y
of new class, she may write something like this:
(define-method change-class ((obj <old-class>) <new-class>) (let ((old-val (slot-ref obj 'x))) (next-method) ;; calls default change-class (slot-set! obj 'y old-val) ;; here, obj's class is already <new-class>. obj))
Updating an instance for a redefined class is also handled
as class change. When an object is accessed via normal
slot accessor/modifier, its class is checked whether
it has been redefined. And if it has indeed been redefined,
change-class
is called with the
redefined class as new-class; that is, updating
an instance is regarded as changing object’s class from
the original one to the redefined one.
By specializing change-class
, you can customize the
way an instance is updated for a redefined class.
However, you need a special care to write change-class
for class redefinition.
First, the redefinition changes global binding of the class object.
So you need to keep the reference to the old class before
redefining the class, and use the old class to specialize
change-class
method:
;; save old <myclass> (define <old-myclass> <myclass>) ;; redefine <myclass> (define-class <myclass> () ...) ;; define customized change-class method (define-method change-class ((obj <old-myclass>) <myclass>) ... (next-method) ...)
Next, note that the above change-class
method may be
triggered implicitly when you access to obj via
slot-ref
, slot-set!
, class-of
, etc.
If you use such procedures like slot-ref
on obj
again within change-class
, it would trigger the
instance update protocol recursively, which would cause
an infinite loop.
You can only use the methods that doesn’t trigger
instance update, that is, slot-ref-using-class
,
slot-set-using-class!
, slot-bound-using-class?
and current-class-of
.
If you want to carry over a slot whose value is calculated
procedurally, such as a virtual slot, then slot-ref
etc.
might be called implicitly on obj during calculating the
slot value. Actually change-object-class
has a
special protection to detect such a recursion. If that
happens, change-object-class
gives up to retrieve
the slot value and just initializes the slot of the new instance
as if the old slot were unbound.
Customizing instance update is highly tricky business,
although very powerful. You can find some nontrivial
cases in the test program of Gauche source code;
take a look at test/object.scm
.