For Development HEAD DRAFTSearch (procedure/syntax/module):

7.3 Instance

In this section, we explain how to create and use an instance.


7.3.1 Creating instance

Using class object, you can create an instance of the class by a generic function make. A specialized method for standard <class> is defined:

Generic Function: make
Method: make (class <class>) arg …

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.

Generic Function: allocate-instance
Method: allocate-instance (class <class>) initargs

Returns a newly-allocated uninitialized instance of class.

Generic Function: initialize
Method: initialize (obj <object>) initargs

The default initialize method for <object> works as follows:

  • For each initializable slot of the class
    • If (the slot has the :init-keyword slot option AND the keyword appears in initargs): Then the corresponding value is used to initialize the slot
    • Else if the slot has :init-value slot option: Then the value given to the slot option is used to initialize the slot
    • Else if the slot has :init-thunk slot option: Then the thunk is called, and the returned value is used to initialize the slot.
    • Else: The slot is left unbound.

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).


7.3.2 Accessing instance

Standard accessors

Function: slot-ref obj slot

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.

Function: slot-set! obj slot value

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.

Function: slot-bound? obj slot

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.

Function: slot-exists? obj slot

Returns true if obj has the slot named slot.

Function: slot-push! obj slot value

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))))
Function: slot-pop! obj slot :optional fallback

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.

Method: ref (obj <object>) (slot <symbol>)
Method: (setter ref) (obj <object>) (slot <symbol>) value

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.

Fallback methods

Generic Function: slot-unbound
Method: slot-unbound (class <class>) obj slot

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.

Generic Function: slot-missing
Method: slot-missing (class <class>) obj slot :optional value

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.

Special accessors

Function: current-class-of obj

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).

Function: class-slot-ref class slot-name
Function: class-slot-set! class slot-name obj
Function: class-slot-bound? class slot-name obj

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.

Method: slot-ref-using-class (class <class>) (obj <object>) slot-name
Method: slot-set-using-class! (class <class>) (obj <object>) slot-name value
Method: slot-bound-using-class? (class <class>) (obj <object>) slot-name

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).


7.3.3 Describing instance

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:

Generic Function: describe obj

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))
Function: describe-common obj

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>
Function: describe-slots obj

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.

Function: describe-details :optional flag

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.


7.3.4 Changing classes

Class change protocol

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.

Generic Function: change-class
Method: change-class (obj <object>) (new-class <class>)

Changes an object obj’s class to new-class. The default method just calls change-object-class procedure.

Function: change-object-class obj orig-class new-class

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:

  1. A new instance of new-class is allocated by allocate-instance.
  2. For each slot of new-class:
    1. If the slot also exists in old-class, and is bound in obj, the value is retrieved from obj and set to the new instance. (The slot is carried over).
    2. Otherwise, the slot of the new instance is initialized by standard slot initialization protocol, as described in Creating instance.
  3. Finally, the content of the new instance is transplanted to the obj—that is, obj becomes the instance of new-class without changing its identity.

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))

Customizing instance update

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.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT