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.
-
Inline documentation. ROOS supports inline documentation. When the documentation is generated, the class hierarchy will be part of the (linked) documentation. Documentation will be written in pod. For more information see ROOS inline documentation.
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:
-
First, all super classes are instantiated one by one in depth first order.
-
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', usingdef-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
callsoperation2
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.4 2006/05/20 12:18:45 hoesterholt Exp $