Types and object orientation

All values in dfsch are of some type. These types comprise an inheritance hierarchy. Apart from normal hierarchical inheritnce, dfsch provides concept of type specializers (<type-specializer>), which can designate arbitrary subsets of types. Roles are special cases of type specializers which can be used to compose not only behavior, but also instance structure.

In contrast to most object-oriented languages and similarly to CLOS, methods are not part of classes or types, but of so called generic functions. This allows user to extend behavior of any existing class regardless of whether it's definition is accessible. Also, methods can be specialized on arbitrary type specializers and not only concrete types, moreover it's possible to dispatch on any argument of generic function or even multiple at once.

Defining generic functions and methods

Generic functions are defined using define-generic-function macro, which is necessary only to pass optional arguments to generic function constructor such as documentation string or different method combination function.

For example:

(define-generic speak)
(define-generic draw
                :documentation "Draw an representation of given object")

Generic function without any methods is not especially useful, methods can be added by define-method macro. It's syntax is similar to normal define, but allows one to add so called qualifiers (which can be used to pass data to method combination function and thus influence role of this method in really executed code) and specializers to method or it's arguments respectively. Specializers place restrictions on argument types for which this method is called.

(define-method (speak (who <cat>))
  (display "Nyaaa!")
  (newline))

(define-method (draw (what <cat>) (where <port>))
  (display "=^.^=" where)
  (newline where))

(define-method (draw (what <cat>) (where <gd:image>))
  ...)

Inside methods, macro call-next-method can be used to defer processing to next less specialized method (similarly to super or so in other languages). Calling call-next-method without any arguments passes arguments originally passed to containing method, presense of arguments to call-next-method overrides this behavior.

Methods without any qualifiers are called primary Default method combination allow use of following qualifiers:

For example:

]=> (define-method ((foo :before) x)
  ..> (display "()")
  ..> (newline))
#<method 0xa8b270 ((foo :before))>
]=> (define-method ((foo :before) (x <number>))
  ..> (display "<number>")
  ..> (newline))
#<method 0xa8b0c0 ((foo :before) <number>)>
]=> (define-method ((foo :before) (x <fixnum>))
  ..> (display "<fixnum>")
  ..> (newline))
#<method 0xadcf00 ((foo :before) <fixnum>)>
]=> (foo 1)
<fixnum>
<number>
()
()

Method combination functions

Method combination function allow user to influence what methods are actually called by providing function that converts list of matching methods into actually called function - simple example of why this is useful is calling all matching primary methods at once instad of only most specialized and combining their results together, method combination function doing exactly that can be produced by make-simple-method-combination.

Defining classes

User defined classes are defined using define-class macro. Apart from name and superclass, this macro requires list of slots of new class (often known as instance variables). Each slot declaration can specify several options:

And additionaly:

For example:

(define-class <animal> ()
  ((:weight :initarg :weight :reader animal-weight)))
(define-class <pet> <animal>
  ((:name :initarg :name :accessor pet-name)))
(define-class <cat> <pet>
  ((:color :initarg :color :reader cat-color)
   (:fur-length :initarg :fur-length :reader cat-fur-length)))

After slot list, additional class options may be present:

Roles

Apart from roles being usable for specialization of methods, they can also contain slots and class options, that are inserted into classes implementing them. In contrast to classes, roles support multiple inheritance.

New role is defined by define-role macro. Building on previos example we can do this:

(define-role <<furry>> ()
  ((:color :initarg :color :reader cat-color)
   (:fur-length :initarg :fur-length :reader cat-fur-length)))
(define-class <cat> <pet>
  ()
  :roles (<<furry>>))
(define-class <dog> <pet>
  ()
  :roles (<<furry>>))
(define-class <boar> <animal>
  ()
  :roles (<<furry>>))

Generated by docgen.scm running on dfsch 0.4.0 (bp-0.4.0-18-g88df03b) on 2012-04-16 10:00:28