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.
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, a class is defined.
def-class provides a strict
template to define a class, which is a followup of directives
constructor and optionally
explicit-cleanup parts. Each directive makes up for a part of the class definition.
this, the definition of the class is given.
this can be used
internally in the functions of the class to refer to its instantiated
(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
this part can contain extra declarations, that will be part of
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, the used super classes are instantiated. They can be called
with parameters as defined in the
this part, as can be seen from the
supers can be used to explicitly call members of
super classes from withing an object.
The definitions in the
private part can only be accessed from within
the current defined class (or closure). They are invisible to the
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 part is used to initialize an instantiated object.
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
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 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 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.
By calling the defined function in the
this part of the class definition,
an object is instantiated. Objects are instantiated in the following order:
First, all super classes are instantiated one by one in depth first order.
After instantiating the super classes, the
thisreference 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.
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 >
operation2in 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.
def-generic, a macro can be defined, that wraps
-> macro. For example:
(def-generic x2) (def-generic a mem-a) >(mem-a obj 3) 3 >(x2 obj) 8 >(mem-a obj) 9
def-generic with one argument
arg1, a macro is defined,
that calls a member function that is named after
def-generic with two arguments
macro is defined with name
arg2, that calls member function
The ROOS API
obj is an instantiated Roos object;
var is a Roos class definition;
(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
If any super of
obj is of type
obj is of type
this function returns
(object? obj) and
(or (symbol? class) (procedure? class)).
#t, if the given object
obj has been explicitly cleaned up.
Returns the tree of classes of which a object is made of. E.g., for
> (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.
cleanup member of
obj (without arguments), if it exists,
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
Is equivalent to
(-> 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.
(roos-add-proxy obj obj-to-proxy-for) : obj
This will add all public members of
obj-to-proxy-for to the proxy
obj. If a given member cannot be found in the object
itself, it will be searched in the proxy classes.
(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])
named-argument. This alias to
is in a remote way analogue to the perl way to do named arguments.
(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 220.127.116.11 2005/12/30 00:31:13 HansOesterholt Exp $