Contents

    ROOS Manual
        Features
    An Example of a Roos class definition
    Class definition parts
        def-class
        this
        supers
        private
        public
        constructor
        explicit-cleanup
    Object instantiation
    Class Members
        Calling members
        Accessing member variables
        "Generic" functions
    The ROOS API
        predicates
            (object? obj)
            (class? var)
            (class=? obj class)
            (roos-invalid? obj)
        Introspection
            (class obj)
            (->> obj function)
            (roos-id obj) : number
        Destruction
            (roos-cleanup obj)
            (-< obj)
        Calling
            (-> obj function ...)
            (roos-dispatch obj symbol-for-member . args)
        Proxy objects
            (roos-add-proxy obj obj-to-proxy-for) : obj
        Named arguments
            (named-argument name:symbol arguments:list [default-value])
            (=> name:symbol arguments:list [default-value])
    Info

ROOS Manual

ROOS is an acronym for "Rose Object Orientation for Scheme". It is a simple and small OO system that can be used for OO easy programming. Without comments, the code is only about 350 lines of scheme code and some 700 lines of C code. ROOS is remotely modelled after the Perl OO model.

Features

Although simple, ROOS has a lot of features.

  • In ROOS, a class definition is actually a function definition. When called, this function returns a closure, with which it has instantiated an 'object'.

  • Within the bounds of a ROOS object, all functions that are local to a class can be called directly. They are within the 'class closure'.

  • ROOS knows private methods, public methods, private member variables and public member variables.

  • ROOS has limited generic function capabilities. It will not make existing scheme definitions generic (e.g. '+'). Instead, it defines a macro, that converts a generic function call on an object to the '->' representation of this call.

  • ROOS has three kinds of object member calling interfaces (to the same object).

    A scheme variable representing an instantiated class is an object. Public methods in the class and it's supers are available to the object by calling them through a generic function or the '->' function.

    The 'this' variable can be used from within a function defined in the class. When a public method is called through the 'this' variable, it is virtual, i.e. the method is called through the external object that is represented by 'this' .

    The 'supers' variable can be used from within a function defined in the class. It is used to call a method that is defined in the super classes of a class. This call will not be 'virtualized'.

  • All member variables or methods that are marked 'public', are virtual, when called via an object interface, except when called via the 'supers' object interface.

  • All private functions are only available within the context of a class closure itself. They cannot be called through an object, this, or supers.

  • ROOS provides macros to support named variables processing.

  • ROOS has object-proxying support. It is possible with ROOS, to proxy methods of other objects through an existing object by just adding the other objects to the proxy list of the given object.

    Proxy support is not the same as inheritance. It basically allows for adding a member variable of type ROOS to the external interface of the class which holds the member variable. It's implementation is currently quite static, in that object proxying doesn't allow for removing the proxied objects.

    Object proxying is especially usefull in situations, that inheritance cannot be used, but it is desirable to have the proxied object's methods at the interface level of the object, which holds the proxied object.

    An example is a class that derives from two classes that derive from the same base class. The public members of the base class are virtual (all public members in ROOS are virtual). If a member of the base class is called, it will be a virtual call, desending from the outside of an object to the base object. Calling this member from either base object level will result in a call to only one of the base objects members. Using proxying, this situation can be avoided.

An Example of a Roos class definition

   >(def-class
      (this (a-class a b) (c 'hello))
      (supers)
      (private
         (define h (* a b))
         (define x 0))
      (public
         (define (set a) 
             (set! x (* a h)))
         (define (get-plus a) (+ (-> this get) a))
         (define (get) x))
      (constructor
         (display c)(newline)
         (set! x h)))

   >(def-class 
      (this (x2 c))
      (supers (a-class c c))
      (private)
      (public
         (define (get) (* (-> supers get) (-> supers get))))
      (constructor))

   >(define obj (x2 4))
   hello
   >(display (-> obj get))
   256
   >(def-generic set)
   >(def-generic get)
   >(set obj 2)
   >(display (get obj))
   1024
   >(class? x2)
   #t
   >(class obj)
   (x2 (a-class))
   >(class=? obj 'b-class)
   #f
   >(class=? obj a-class)    ; Note it also works without symbols
   #t
   >(object? obj)
   #t
   >(-> obj get-plus 10)
   1034

Class definition parts

def-class

With def-class, a class is defined. def-class provides a strict template to define a class, which is a followup of directives this, supers, private, public, constructor and optionally explicit-cleanup parts. Each directive makes up for a part of the class definition.

this

With this, the definition of the class is given. this can be used internally in the functions of the class to refer to its instantiated object. e.g.:

    (def-class 
     (this (operation2 _a _b))
     (supers)
     (private
        (define a _a)
        (define b _b))
     (public
        (define (operation2 a b) 'nil)
        (define (calculate) (-> this operation2 a b)))
     (constructor))

    (def-class 
     (this (add a b))
     (supers (operation2 a b))
     (private)
     (public
        (define (operation2 a b) (+ a b)))
     (constructor))

    > (define a (add 3 4))
    > (-> a calculate)
    7
    >

In this example, (-> this operation2 a b) calls the virtual function operation2.

The this part can contain extra declarations, that will be part of a let* construct. Example:

    >(def-class
      (this (x2 _a _b) (c (* _a _b)) (d #f))
      (supers)
      (private
         (define a _a)
         (define b _b))
      (public
         (define (mutated) d)
         (define (x2-orig) c)
         (define (x2 . args) 
            (if (null? args)
               (* a b)
               (begin
                  (set! d #t)
                  (set! a (car args))
                  (set! b (cadr args))
                  (x2)))))
      (constructor))
    >(define a (x2 4 3))
    >(-> a x2-orig)
    12
    >(-> mutated)
    #f
    >(-> a x2 5 6)
    30
    >(-> a x2)
    30
    >(-> a x2-orig)
    12
    >(-> a mutated)
    #t
    >(-> a x2 4 3)
    12
    >(-> a mutated)
    #t

supers

With supers, the used super classes are instantiated. They can be called with parameters as defined in the this part, as can be seen from the previous example. supers can be used to explicitly call members of super classes from withing an object.

private

The definitions in the private part can only be accessed from within the current defined class (or closure). They are invisible to the outside world.

public

All definitions in the public part can be accessed both from within the current closure and from the outside world. Additionally, all definitions, when called through this are virtual, i.e., the member functions or variables they represent will be searched starting at the outer side of an instantiated object.

constructor

The constructor part is used to initialize an instantiated object. There's no destructor part, as all variables are collected using garbage collection. If explicit destruction is needed (e.g., because a file needs to be closed), an optional explicit-cleanup part can be used. roos-cleanup or -< can be used to destruct an object explicitly. Construction is done from inside to outside, i.e., first all supers are constructed (from left to right), then, the current class is constructed.

explicit-cleanup

An optional explicit-cleanup part can be added to be used as a destructor. explicit-cleanup parts are called outside to inside, i.e. first the current object closure is destructed, then, its supers (from left to right).

It is possible to do virtual calls from within the inner parts of the object being cleaned. However, these calls must take into account, that object data of outher parts may already have been cleaned and invalidated.

roos-cleanup calls the explicit-cleanup parts. It also invalidates the given object, so that it can no longer be used.

As of version 0.30 of ROOS, roos-cleanup has been made a member function of a ROOS object itself. So it is possible to delete this using, (- this roos-cleanup)>, which will call roos-cleanup from outside in. The whole object structure is invalidated using this call, and from outside in, all explicit-cleanup parts are executed before invalidating that specific part of the structure.

Object instantiation

By calling the defined function in the this part of the class definition, an object is instantiated. Objects are instantiated in the following order:

  1. First, all super classes are instantiated one by one in depth first order.

  2. After instantiating the super classes, the this reference to the current closure is propageted down to all instantiated 'super' objects, after which the constructor of the current class is called.

Note, that while instantiating a class, the this operator in (-> this ...) operates only from the current class on to the super classes. There are no virtual functions at this point.

Class Members

Calling members

Member functions can be called using a number of different constructs:

  • (-> object member arg1 ...): This construct can be used to call a member of a given object. This is the most standard way to call a member function.

  • (->> object member): This construct can be used to get the procedure associated with the given member. <(procedure? (->> object member))> will be true.

  • (member object arg1 ...): This construct can be used to call a member function of a given object, after the member function has been made 'generic', using def-generic member); it transforms into (-> object member arg1 ...).

  • (-> this member arg1 ...): This construct can be used to call a member function from within an object. Use it to call a member as if it was called from outside of the object.

  • (-> supers member arg1 ...): This construct can be used to call a member of the object its 'super' objects.

  • (member arg1 ...): Use this construct to call a member function in the current object ('class closure'), e.g.:

        (def-class 
        (this (operation2 _a _b))
        (supers)
        (private
          (define a _a)
          (define b _b))
        (public
          (define (operation2 a b) 'nil)
          (define (calculate) (-> this operation2 a b))
          (define (calcthis) (operation2 a b)))
        (constructor))
    
        (def-class 
        (this (add a b))
        (supers (operation2 a b))
        (private)
        (public
          (define (operation2 a b) (+ a b)))
        (constructor))
    
        > (define a (add 3 4))
        > (-> a calculate)
        7
        > (-> a calcthis)
        nil
        >
    

    Member calcthis calls operation2 in the current closure context.

Accessing member variables

Public member variables can only be accessed from the outside as if they were member functions. E.g.:

    (def-class 
    (this (sample _a))
    (supers)
    (private)
    (public
      (define a _a)
      (define (x2) (set! a (* a a)) (- a 1)))
    (constructor))

    >(define obj (sample 4))
    >(-> obj a)
    4
    >(-> obj a 20)
    20
    >(-> obj a)
    20
    >(-> obj x2)
    399
    >(-> obj a)
    400

From within the current closure, a member function can use the member variable as if it were a standard variable. This is illustrated in the previous sample, using the x2 member function.

"Generic" functions

Using def-generic, a macro can be defined, that wraps the -> macro. For example:

    (def-generic x2)
    (def-generic a mem-a)

    >(mem-a obj 3)
    3
    >(x2 obj)
    8
    >(mem-a obj)
    9

Using def-generic with one argument arg1, a macro is defined, that calls a member function that is named after arg1.

Using def-generic with two arguments arg1 and arg2, a macro is defined with name arg2, that calls member function arg1.

The ROOS API

predicates

(object? obj)

Returns #t, if obj is an instantiated Roos object; #f, otherwise.

(class? var)

Returns #t, if var is a Roos class definition; #f, otherwise.

(class=? obj class)

Given an object obj and a class-name class, which must be a symbol or the defined function for the class, determines if obj is of type class. If any super of obj is of type class, or obj is of type class, this function returns #t, returns #f, otherwise.

Preconditions: (object? obj) and (or (symbol? class) (procedure? class)).

(roos-invalid? obj)

Will return #t, if the given object obj has been explicitly cleaned up.

Introspection

(class obj)

Returns the tree of classes of which a object is made of. E.g., for class add:

    > (class a)
    (add (operation2))

(->> obj function)

This construct is used to retreive a specific member function from an object. Using this construct can speed up things considerably (about a factor 2 for calling overhead).

(roos-id obj) : number

Returns the id of the given object.

Destruction

(roos-cleanup obj)

Calls the cleanup member of obj (without arguments), if it exists, and invalidates obj. After obj has been invalidated, it cannot be used anymore. Example:

    (def-class 
     (this (class-a a))
     (supers)
     (private
      (define counter a))
     (public
      (define (cl) this)
      (define var (* a a))
      (define (inc b) (set! counter (+ counter b)))
      (define (get) counter)
      (define (count) (begin
                        (-> this inc 1)
                        (inc 2))))
     (constructor
      (set! counter (* counter counter)))
     (explicit-cleanup (display "class-a says HI! - ")(display counter)(newline)))

    >(define a (class-a 32))
    >a
    #roos
    >(-< a)
    class-a says HI! - 1024
    >a
    #roos
    >(-> a get)
    ROOS:c-roos-call-member object has been invalidated (argument 0)
    >(roos-invalid? a)
    #t

(-< obj)

Is equivalent to (roos-cleanup obj).

Calling

(-> obj function ...)

Is used to call member functions.

(roos-dispatch obj symbol-for-member . args)

This function is used to dynamic dispatching of members. The member is provided as a symbol.

Proxy objects

(roos-add-proxy obj obj-to-proxy-for) : obj

This will add all public members of obj-to-proxy-for to the proxy list of obj. If a given member cannot be found in the object itself, it will be searched in the proxy classes.

Named arguments

(named-argument name:symbol arguments:list [default-value])

Can be used to get a named value from an argument list. Example:

    >(named-argument 'text (list 'text "3" 'number 42) "no")
    "3"
    >(named-argument 'test (list 'text "3" 'number 42) "no")
    "no"
    >(named-argument 'test (list 'text "3" 'number 42))
    #f
    >(named-argument 'yes (list 'text "3" 'number 42 'yes) '%noyes)
   '%roos-exists
    >(named-argument 'yes (list 'text "3" 'number 42 'yes 'nopo 665) '%noyes)
    'nopo
    
    (define (f . args)
        (if (eq? (named-argument 'mandatory-argument args 'undef) 'undef)
           (error "He, you're missing a mandatory argument!")
           #t))

Note: If you are only interested if a certain named argument is there, i.e., you're using it as a kind of flag, you best test for a value as follows:

    >(named-argument 'text (list 'text "3" 'number 42) "no")
    "3"
    >(named-argument 'test (list 'text "3" 'number 42) "no")
    "no"
   >(let* ((args (list 'a (list 2 43 1) 'flag 'flag1 'flag3))
            (flag1 (named-argument 'flag1 args '%flag-not-there))
            (flag2 (named-argument 'flag2 args '%flag-not-there)))
       (set! flag1 (not (eq? flag1 '%flag-not-there)))
       (set! flag2 (not (eq? flag2 '%flag-not-there)))
       (display (format "flag1=~a ~%" flag1))
       (display (format "flag2=~a ~%" flag2))
       (list flag1 flag2))
    flag1=#t
    flag2=#f
    (#t #f)
    >

(=> name:symbol arguments:list [default-value])

Same as named-argument. This alias to named-argument is in a remote way analogue to the perl way to do named arguments.

Info

(c) 2005 Hans Oesterholt-Dijkema. Distributed undef LGPL. Contact: send email to hans in domain elemental-programming.org. Homepage: http://www.elemental-programming.org.

$Id: roos_manual.pod,v 1.22.2.4 2005/12/30 00:31:13 HansOesterholt Exp $