You are looking at historical revision 8517 of this page. It may differ significantly from its current revision.

Introduction

meroon is a port of Christian Queinnecs Meroon object system.

Author

Christian Queinnec (<Christian.Queinnec@polytechnique.fr> or <Christian.Queinnec@INRIA.fr>), ported to Chicken by felix.

History

The Meroon version is Meroon V3 Noel+2 $Revision: 1.1 $

   
   * 1.92 Changes to stop annoying messages [Kon Lovett]
   * 1.91 Bug fix in method defining macros [Thanks Matt Gushee]
   * 1.9 Generates an .exports file and install documentation. Fixed bug in meroon.scm [Thanks to Stu Glaser]
   * 1.8 The code is now compiled with -no-trace instead of -d0
   * 1.7 check-class signals error when call with a non-object [Thanks to Brad Lucier]
   * 1.6 meroon-error signals a (exn meroon) condition
   * 1.5 Works now in conjunction with the highlevel macro system (syntax-case)
   * 1.4 Adapted to new setup scheme
   * 1.3 Setter syntax is now ...-set! instead of set-...!; allows now native keywords to be used
   * 1.2 Removed separate library, everything is now included by default
   * 1.1 Loads (meroon library) automatically, when interpreted or at compile time
   * 1.0 

Usage

(require-extension meroon)

When compiling a file that uses meroon, you should also add the -X meroon or -extend meroon option to csc:

csc -X meroon file.scm

Requirements

Documentation

What follows is (a slighlty reformated version of) the official manual page for Meroon. Here a list of things that apply to this particular port:

Description

MeroonV3 is an object system written in Scheme that works under Chicken, Bigloo, Elk, Gambit, Guile, MacScheme, MIT-Scheme, OScheme, PC Scheme, PCS/geneva, Scheme->C, SCM and vscm interpreters. It also runs faster when compiled with Bigloo, Gambit and Scheme->C, see below.

The MeroonV3 object system was originally invented for a book (in French (InterEditions) and English (Cambridge U. Press)) to describe implementations of Lisp and Scheme. It was designed to have a pedagogical, portable but efficient implementation, to support separate compilation yet to be powerful enough to unify all the data types of Scheme even vectors and strings without restriction of inheritance. It thus offers the concept of classes with regular or indexed fields, a somewhat static initialization protocol, meta­ classes with code-generation capabilities, generic func­ tions with multimethods among other features.

The latest version of MeroonV3 can be retrieved by anony­ mous ftp from ftp.inria.fr (IP number 192.93.2.54) under subdirectory INRIA/Projects/icsla/Programs. Questions about MeroonV3 (or older versions: MeroonV2 and Meroonet) should be directed towards the mailing list: <meroon-info@cornas.inria.fr> Ask to be enlisted in that mailing list by sending a mes­ sage to: <meroon-request@cornas.inria.fr>

Quick Reference Card

MeroonV3 basically offers three macros to define classes, generic functions and methods for them, it also offers one form to create instances. Generic functions support multi­ methods. By default, many accompanying functions are cre­ ated when definining a class. To not bother those who know what objects mean, the next section tells how to use Meroon by example.

Meroon By Example

Define class Point with fields x and y, Object is the root class.

(define-class Point Object (x y))

recognizer and maker at work.

(Point? (make-Point 33 22))  ; -> #t

Define an heir to Point with an additional indexed field.

(define-class NamedPoint Point
   ((* name :immutable)) )

selector and handy instantiation (with keywords) at work.

(Point-x (instantiate NamedPoint
          :name 'joe 'jill
          :y 33 :x 222 ))    ; -> 222

Define another class with an explicit field initializer (a thunk).

(define-class ColoredNamedPoint NamedPoint
  ((= color :initializer (lambda () 'pink))) )

(ColoredNamedPoint-color
 (instantiate ColoredNamedPoint :name 'joe 'jill :y 33 :x 222) )  ; -> pink

maker also exist for indexed fields as well as modifiers. Field x can be retrieved as Point-x or ColoredNamedPoint-x.

(let ((pt (make-ColoredNamedPoint 11 22    2 'joe 'jack   'red)))
  (set-NamedPoint-x! pt '555)
  (ColoredNamedPoint-x pt) )         ; -> 555

Define a generic function with a dotted variable.

(define-generic (foo a (b) . stream)
  (apply display "default" stream) )

Add a (congruent) method. It is possible to inquire the length of the indexed field. It is also possible to call the next method.

(define-method (foo x (np NamedPoint) . str)
  (apply display (NamedPoint-name-length np) str)
  (call-next-method) )

(foo (make-ColoredNamedPoint 11 22    2 'joe 'jack   'red)
     (instantiate Point :y 2 :x 3) )      ; prints "default"

Use of the predefined generic function clone.

(foo (clone (make-Point 3 2))
     (instantiate ColoredNamedPoint
       :color 'red :name 'joe 'jack :x 11 :y 22 ) )  ; prints "2" then "default"

Define a generic function with multimethods and restricts methods to be added to subclasses of Point * Point.

(define-generic (dist (o1 Point) (o2 Point)))

(define-method (dist (o1 NamedPoint) (o2 ColoredNamedPoint))
   (eq? (ColoredPoint-color o1) (ColoredPoint-color o2)) )

Defining Classes

The define-class macro defines a new class named <name-of- the-class>, subclass of <name-of-the-super-class>. Instances of this new class will contain all the fields specified by the super-class <name-of-the-super-class> plus the proper fields, those that appear in the define- class form. A field specified as <name-of-a-Mono-Field> can be equivalently specified as (= <name-of-a-Mono- Field>). A Mono-Field is qualified with an equal sign and only holds a single value. A Poly-Field (or indexed field) is qualified by a leading star and holds as many values as specified when allocating instances. Different instances can have a different number of values in their Poly-Fields. Fields can be mutable or immutable (no modi­ fiers are then created for them); initializers can be specified. It is also possible to mention that the field may be uninitialized: every allocation will therefore be checked to initialize this field. The metaclass can be imposed, by default it is the MeroonV2-Class.

(define-class <name-of-the-class> <name-of-the-super-class>
       ( <name-of-a-Mono-Field>                        |
         (= <name-of-a-Mono-Field>
            [ :immutable | :mutable ] [ :maybe-uninitialized ]
            [ :initializer (lambda () value) ] )       |
         (* <name-of-a-Poly-Field>
            [ :immutable | :mutable ] [ :maybe-uninitialized ]
            [ :initializer (lambda (index) value) ] )
         ... )
    [ :metaclass <name-of-the-meta-class> ]
    [ :immutable ] )

When a class is defined and if it has the default meta­ class then many accompanying functions are also defined: a predicate, field readers, field writers, indexed field length readers as well as a general maker. The signatures of these functions follow:

(<class-name>? value)

(make-<class-name> <value-for-a-mono-field>
                 | <length> <<length>-values-for-a-poly-field>
                 ... )

(<class-name>-<mono-field-name> object)

(<class-name>-<poly-field-name> object index)

(set-<class-name>-<mono-field-name>! object value)

(set-<class-name>-<poly-field-name>! object index value)

(<class-name>-<poly-field-name>-length object)

The class object is itself an indirect instance of the Class metaclass. It can be obtained via the global vari­ able <class-name>-class.

A generic function, named -><class-name>, is also defined to hold coercion methods converting values to instances of <class-name>.

Defining Generic Functions

The define-generic form defines a generic function named <name-of-the-generic-function>, with a list of variables defined by <description-of-the-variables>. If no appro­ priate method is found when the generic function is invoked, the <optional-default-method> is invoked or an anomaly is provoked. The <description-of-the-variables> is similar to the list of variables of regular lambda forms except that discriminating variables appear sur­ rounded by parentheses. There can be more than one dis­ criminating variable.

(define-generic ( <name-of-the-generic-function>
                  . <description-of-the-variables> )
      [ <optional-default-method> ]   )

<description-of-the-variables>
    ::= ( <variable-name>
          . <description-of-the-variables> )
     |  ( ( <variable-name> [<maximal-class-allowed>] )
          . <description-of-the-variables> )
     |  <variable-name>

The define-method form defines a method on an already existing generic function. The <description-of-the-vari­ ables> must be congruent to that of the generic function and any discriminating variable must specify a <class- name> compatible with the <maximal-class-allowed> if pre­ sent in the generic function. The <body-of-the-method> may use the (call-next-method) form to invoke the method that should have been invoked if the present one was not there. It is also possible to use the (next-method?) predicative form to determine if there is a method that can be invoked by (call-next-method).

(define-method ( <name-of-the-generic-function>
                 . <description-of-the-variables> )
      <body-of-the-method> )

<description-of-the-variables>
    ::= ( <variable-name>
          . <description-of-the-variables> )
     |  ( ( <variable-name> <class-name> )
          . <description-of-the-variables> )
     |  <variable-name>

A restriction on multimethods is that it is forbidden to define a method that would bring ambiguity. This may only occur when defining a multimethod on classes A' x B when a multimethod is already defined on A x B' and A' is a sub­ class of A while B' is a subclass of B. The method to apply on A' x B' would be ambiguous.

Allocating Objects

To ease the allocation of objects, a macro exists that provides keywords to define fields in whatever order is felt convenient. Any unspecified field gets its initial value from its corresponding initializer if mentioned in the class definition otherwise the field is left unini­ tialized (that is only possible if the :maybe-uninitial­ ized field option is specified for that field in the class definition) and the associated reader will provoke an anomaly if trying to read such an uninitialized field. The initialize! function is eventually invoked on the result­ ing object.

(instantiate <class-name>
    :<mono-field-name>        value
    :<poly-field-name>        values ...
    :<poly-field-name>-length natural
    ... )

Some other macros exist to ease the creation of instances. The duplicate macro allows you to create a new object based on the differences to bring to an original object. The co-instantiate macro allows you to instantiate multi­ ple objects at the same time, these objects may contain mutual (or recursive, or cyclic) references.

Predefined Generic Functions

The following general utility functions exist to access fields. A field can be read if it has a value. A field can be initialized if it has no value. A field can be inspected to know if it is initialized. A field can be modified if associated to the :mutable field option or, not associated to the :immutable field option (by default, any field is mutable). In any other cases, an anomaly is signalled.

(field-value object mono-field)              -> value
(field-value object poly-field index)        -> value

(set-field-value! object value mono-field)
(set-field-value! object value poly-field index)

(initialize-field-value! object value mono-field)
(initialize-field-value! object value poly-field index)

(field-length object poly-field)             -> length

(field-defined? object mono-field)           -> boolean
(field-defined? object poly-field index)     -> boolean

The generic function clone returns a shallow copy of any object. The generic function initialize! is invoked on every freshly built instance. The generic function show displays objects. These functions are there to be cus­ tomized.

(clone object)          -> object
(initialize! object)    -> object
(show object [stream])

There exist also some predefined coercers like ->Class that converts names (symbols) into classes, ->Generic that converts names into generic instances.

Predefined Classes

These are some of the predefined classes with their fields. The accompanying functions exist. You can read all these fields but it is dangerous to modify them even if they are mutable!

(define-class Object #f ())
(define-class Class Object
  (name number fields super-number subclass-numbers
        allocator immutable? min-son max-son (* super) ) )
(define-class Handy-Class Class ())
(define-class MeroonV2-Class Handy-Class ())
(define-class Generic Object
  (behavior name default variables dispatcher top-classes) )
(define-class Generic-1 Generic ())
(define-class Generic-N Generic ())
(define-class Field Object
  (immutable? name class-number initialized? initializer (* path)) )
(define-class Mono-Field Field ())
(define-class Poly-Field Field ())
(define-class Anomaly Object (category operator message (* hint)))

Library

The class of any object can be retrieved using object->class. An object can be tested for class-member­ ship with is-a?. Classes can be related through subclass?. A generic comparator named egal allows to compare any two objects (even circular) to determine if they are equiva­ lent.

(object->class object)     -> class
(is-a? value class)        -> boolean
(subclass? class class  )  -> boolean
(egal object object)       -> boolean

Debugging

You can trace generic functions or inquire your state or any object with the following functions:

(show-generic-trace <generic-name> ...)

without arguments untrace all traced generic functions

(show-generic-untrace [<generic-name> ...])

(show-meroon)                  ; display which Meroon you are using
(show-hierarchy [class])       ; show [part of] the tree of classes
(show-generic [generic])       ; show methods on [some] generic functions

(unveil object [stream])    ; show all the details of an object (even circular)

Bugs

Find them, fix them, mail them!

Missing Features

MeroonV3 still refuses to provide multiple inheritance.

License

Copyright (c) 1990-96 by Christian Queinnec. All rights reserved.

This code is released under the Library GNU Public License