Object SystemBigloo's object system is designed to be as simple as possible and belongs to the Clos [Bobrow et al. 88] object system family in that it uses classes, generic functions and methods. Its design has been strongly influenced by C. Queinnec's Meroon [Queinnec93] It does not include any meta object protocol.
Class declarationClasses are defined in a module declaration. A class declaration can take place in a compiled or interpreted module. If a class declaration takes place in a static module clause (see Section Module Declaration) its scope is limited to the current module but if it takes place in an export module clause, its scope is extended to all modules that import the current module. The syntax of a class declaration is:
class ident field ...bigloo module clause
<class> ⇒A class is a Bigloo type (see Section Atomic types) and the class identifier is extracted from the <ident> of the class definition. If <ident> is also a <typed-ident>, the type part of this identifier denote the super-class of the class. If <ident> is a <Ieee-ident>, the super-class of the class is the root of the inheritance tree, the
(class<ident> <constructor>? <field>+
(final-class<ident> <constructor>? <field>+
(wide-class<ident> <constructor>? <field>+
(abstract-class<ident> <constructor>? <field>+
)<field> ⇒ <ident> |
objectclass is the only pre-existing class. Final classes can only be sub-classed by wide classes. Wide classes (only for compiled modules) can only inherit from final classes. abstract classes can't be instantiated. Wide-classes cannot be defined within the interpreter. The optional constructor is an expression that must evaluate to a one argument function. This function is automatically invoked each time a new class instance is created. The constructor is passed the fresh instance. If a class has not defined a constructor the super class' constructors are searched. The first constructor found is invoked. A constructor may be a generic function with a method specified for one or more classes. A class field may be a typed class field which is achieved by using a <typed-ident> instead of a <Ieee-ident> for the <ident> value. Field marked with
read-onlydeclaration are immutables.
Defaultdeclarations allow default field values. For the means of an example, the traditional points and colored points can be defined as:
(module example (static (abstract-class pt) (class point::pt x::double y::double) (class point-C::point (color::string read-only))))We illustrate final and wide classes by the example:
(module example (export (final-class person (name::string (default "Jones")) (sex read-only) children::pair-nil) (wide-class married-person::person mate::person)))Fields may be virtual. A field is virtual as soon as its declaration contain a
getattribute. Virtual fields have no physical implementation within the instance. When defining a virtual field, the class declaration implements a getter and a setter if that field is not a read only field. Access to the virtual field will rely on invocation of the user getter and user setter. For instance:
(module example (static (class complex mag::double angle::double (real::double (get (lambda (p) (with-access::complex p (mag angle) (* mag (cos angle))))) read-only) (imag::double (get (lambda (p) (with-access::complex p (mag angle) (* mag (sin angle))))) read-only)))) (let ((p (instantiate::complex (mag 1.0) (angle 2.18)))) (with-access::complex p (real imag) (print "real: " real) (print "imag: " imag)))Virtual fields cannot be associated default values. If a virtual field is not provided with a setter it must be annotated as read only.
Infodeclarations allow arbitrary user information field values. This value can be retrieved by introspection, by the means of the
class-field-infointrospection function. For the means of an example, with add to information to the slot of the point class.
(module example (static (class point (x::double (info '(range 0.0 10.0))) (y::double (info '(range -1.0 1.0)))))
Creating and accessing objectsObjects and classes are created and manipulated via library functions and forms created automatically by Bigloo when a new class is defined.
isa? obj classbigloo procedureThis function returns
#tif obj is an instance of class or an instance of a sub-class of class, otherwise, it returns
instantiate::class (ident value)...bigloo syntaxThis forms allocates object of class class and fills the fields with values found in the list of parameters (note that field are explicitly named and that there is no ordering for field naming). Field values which are not provided in the parameter list must have been declared with a
defaultvalue which is used to initialize the corresponding field. For instance:
(module example (export (class point (x (default 0))) (class point2d::point y))) (instantiate::point (x 0) (y 0)) (instantiate::point (y 0)) (instantiate::point (x 0)) ⇒ Error because y has no default value
class-nil classbigloo procedureThis function returns the NIL pre-existing class instance. This instance plays the role of
void *in C or
nullin Java. The value of each field is unspecified but correct with respect to the Bigloo type system. Each call to
class-nilreturns the same object (in the sense of
(module example (export (class point x) (class point2d::point y))) (eq? (class-nil point) (class-nil point)) ⇒ #t (eq? (class-nil point) (class-nil point2d)) ⇒ #f
with-access::class obj (binding...) bodybigloo syntaxA reference to any of the variables defined in as a binding is replaced by the appropriate field access form. This is true for both reference and assignment. A binding is either a symbol or a list of two symbols. In the first place, it denotes a field. In the second case, it denotes an aliases field. For instance:
(with-access::point p (x (y1 y)) (with-access::point p2 (y) (set! x (- x)) (set! y1 (- y1 y))))
-> var field bigloo syntaxClass instances can be accesses using the
->special form. The the first argument must be the identifier of a local typed variable, otherwise an error is raised. The form
->can be used to get or set value of an instance field. For instance:
(define (example p1::point p2::point) (set! (-> p1 x) (- (-> p1 x))) (set! (-> p1 y) (- (-> p1 y) (-> p2 y))))This is equivalent to:
(define (example p1::point p2::point) (with-access::point p1 (x (y1 y)) (with-access::point p2 (y) (set! x (- x)) (set! y1 (- y1 y)))))
co-instantiate ((var value) ...) bodybigloo syntaxThis form is only available from compiled modules. In other words, it is not available from the interpreter. It permits the creation of recursive instances. It is specially useful for creating instances for which class declarations contain cyclic type references (for instance a class
c1for a which a field is declared of class
c2and a class
c2for which a class is declared of type
c1). The syntax of a
co-instantiateform is similar to a
letform. However the only legal values are
instantiateforms. The variables introduced in the binding of a
co-instantiateform are bound in body. In addition, they are partially bound in the values expressions. In a value position, a variable var can only be used to set the value of a field of an instantiated class. It cannot be used in any calculus. Example:
(module obj-example (export (class c1 a b o2::c2) (class c2 x y o1::c1))) (co-instantiate ((o1 (instantiate::c1 (a 10) (b 20) (o2 o2))) (o2 (instantiate::c2 (x 10) (y 20) (o1 o1)))) (with-access::c1 o1 (o2) (with-access::c2 o2 (x y) (+ x y)))) ⇒ 30
duplicate::class obj (ident value)...bigloo syntaxThis forms allocates an instance of class class. The field values of the new object are picked up from the field values of the old object unless they are explicitly given in the parameter list. For instance:
(with-access::point old (x) (instantiate::point (x x) (y 10)))is equivalent to:
(duplicate::point old (y 10))The duplicated instance might be a subclass of duplicated instance. Example:
(module foo (export (class point x y) (class point3::point z) (class point4::point3 t)) (main main)) (define (main x) (let ((p (instantiate::point (x 1) (y 2)))) (print (duplicate::point4 p (z 6) (t 55)))))
Generic functionsA generic function is a bag of specific functions known as methods. When invoked on a Bigloo object, a generic function determines the class of the discriminating variable (corresponding to the first argument of the generic function) and invokes the appropriate method. Generic functions implement single inheritance and each is defined using the
define-generic (name arg...) default-bodybigloo syntaxA generic function can be defined with a default body which will be evaluated if no method can be found for the discriminating variable. The default default-body signals an error.
.keepAs an example, here is a possible definition of the
(define-generic (object-display obj::object . op) (let ((port (if (pair? op) (car op) (current-output-port)))) (display "#\|" port) (display (class-name (object-class obj)) port) (display "\|" port)))Methods can be defined to specialize a generic function and such methods must have a compatible variable list. That is, the first argument of the method must be a sub-type (i.e. belong to a sub-class) of the first argument of the generic function. Other formal parameters must be of same types. Moreover, the result type of the method must be a sub-type of the result of the generic function.
define-method (name arg...) bodybigloo syntax
call-next-methodbigloo syntaxIf there is no appropriate method, an error is signaled. Methods can use the form
(call-next-method)to invoke the method that would have been called if not present. The
(call-next-method)cannot be used out of method definition. example:
(define-method (object-display p::person . op) (let ((port (if (pair? op) (car op) (current-output-port)))) (fprint port "firstname : " (-> p fname)) (fprint port "name : " (-> p name)) (fprint port "sex : " (-> p sex)) p))
Widening and shrinkingBigloo introduces a new kind of inheritance: widening. This allows an object to be temporarily widened (that is transformed into an object of another class, a wide-class) and then shrink-ed (that is reshaped to its original class). This mechanism is very useful for implementing short-term data storage. For instance, Bigloo compilation passes are implemented using the widening/shrinking mechanism. On entry to a pass, objects are widened with the specific pass fields and, on exit from a pass, objects are shrunk in order to forget the information related to this pass. Only instances of final classes can be widened and objects can only be widened in order to become instances of wide classes. Widening is performed by the
widen!::wide-class obj (id value) ...bigloo syntaxThe object obj is widened to be instance of the wide class wide-class. Fields values are either picked up from the parameter list of the
widen!form or from the default values in the declaration of the wide class.
.keepObjects are shrunk using the
shrink! objbigloo syntax
.keepHere is a first example:
(module example (static (final-class point (x (default 0)) (y (default 0))) (wide-class named-point::point name))) (define *point* (instantiate::point))Two classes have been declared and an instance
pointhas been allocated. For now,
*point*is an instance of
pointbut not an instance of
named-pointand this can be checked by:
(print (isa? *point* named)) ⇒ #t (print (isa? *point* named-point)) ⇒ #fNow, we widen
(let ((n-point (widen!::named-point *point* (name "orig"))))And we check that now,
n-pointis an instance of
named-pointis a subclass of
n-pointstill is an instance of
(print (isa? n-point named-point)) ⇒ #t (print (isa? n-point named)) ⇒ #tWidening affects the objects themselves. It does not operate any copy operation. Hence,
(print (eq? n-point *point*)) ⇒ #tTo end this example, we shrink
n-pointand check its class.
(shrink! n-point) (print (isa? *point* named-point))) ⇒ #fHere is a more complex example: We illustrate widening and shrinking using our ``wedding simulator''. First let us define three classes,
person(for man and woman),
(module wedding (static (final-class person name::string fname::string (sex::symbol read-only)) (wide-class married-man::person mate::person) (wide-class married-woman::person maiden-name::string mate::person)))As we can see people are allowed to change their name but not their sex. The identity of a person can be printed as
(define-method (object-display p::person . op) (with-access::person p (name fname sex) (print "firstname : " fname) (print "name : " name) (print "sex : " sex) p))A married woman's identity is printed by (we suppose an equivalent method definition for married-man)
(define-method (object-display p::married-woman . op) (with-access::married-woman p (name fname sex mate) (call-next-method) (print "married to: " mate) p))We create a person with the
(define (birth name::string fname::string sex) [assert (sex) (memq sex '(male female))] (instantiate::person (name name) (fname fname) (sex sex)))We celebrate a wedding using the
(define (get-married! woman::person man::person) (if (not (and (eq? (-> woman sex) 'female) (eq? (-> man sex) 'male))) (error "get-married" "Illegal wedding" (cons woman man)) (let* ((mname (-> woman name)) (wife (widen!::married-woman woman (maiden-name mname) (mate man)))) (person-name-set! wife (-> man name)) (widen!::married-man man (mate woman)))))We can check if two people are married by
(define (couple? woman::person man::person) (and (isa? woman married-woman) (isa? man married-man) (eq? (with-access::married-woman woman (mate) mate) man) (eq? (with-access::married-man man (mate) mate) woman)))Now let us study the life a
Smith. Once upon a time...
(define *junior* (birth "Jones" "Junior" 'male)) (define *pamela* (birth "Smith" "Pamela" 'female))Later on, they met each other and ... they got married:
(define *old-boy-junior* *junior*) (define *old-girl-pamela* *pamela*) (get-married! *pamela* *junior*)This union can be checked:
(couple? *pamela* *junior*) ⇒ #tWe can look at the new identity of
(print *pamela*) ⇥ name : Jones firstname : Pamela sex : FEMALE married to: Junior JonesBut
*junior*still are the same persons:
(print (eq? *old-boy-junior* *junior*)) ⇒ #t (print (eq? *old-girl-pamela* *pamela*)) ⇒ #tUnfortunately all days are not happy days. After having been married
(define (divorce! woman::person man::person) (if (not (couple? woman man)) (error "divorce!" "Illegal divorce" (cons woman man)) (with-access::married-woman woman (maiden-name) (begin (shrink! woman) (set! (-> woman name) maiden-name)) (shrink! man)))) (divorce! *pamela* *junior*)We can look at the new identity of
(print *pamela*) ⇥ name : Smith firstname : Pamela sex : FEMALEAnd
*junior*still are the same persons:
(print (eq? *old-boy-junior* *junior*)) ⇒ #t (print (eq? *old-girl-pamela* *pamela*)) ⇒ #t
Classes handlingNo type denotes Bigloo's classes. These objects are handled by the following library functions:
find-class symbolbigloo procedureReturns, if any, the class named symbol.
class? objbigloo procedureReturns
#tif and only if obj is a class.
class-super classbigloo procedureReturns the super-class of class.
class-subclasses classbigloo procedureReturns the subclasses of class.
class-name classbigloo procedureReturns the name (a symbol) of class.
object-constructor classbigloo procedureReturns class's constructor.
object-class objectbigloo procedureReturns the class that object belongs to.
wide-object? objectbigloo procedureReturns
#tif object is a wide object otherwise it returns
object-display object [port]bigloo genericThis generic function is invoked by
displayto display objects.
object-write object [port]bigloo genericThis generic function is invoked by
writeto write objects.
object->struct objectbigloo generic
struct->object structbigloo procedureThese functions converts objects into Scheme structures and vice-versa.
object-equal? object objbigloo genericThis generic function is invoked by
equal?when the first argument is an instance of
object-hashnumber objectbigloo genericThis generic function returns an hash number of object.
isa? obj classbigloo procedureReturns
#tif obj belongs to class otherwise it returns
Object serializationObjects can be serialized and un-serialized using the regular
obj->stringfunctions. Objects can be stored on disk and restored from disk by the use of the
input-objfunctions. In addition to this standard serialization mechanism, custom object serializers and un-serializers can be specified by the means of the
register-class-serialization!function (see Section Serialization.
EqualityTwo objects can be compared with the
equal?function. Two object are equal if and only if they belong to a same class, all their field values are equal and all their super class's field values are equal.
IntrospectionBigloo provides the programmer with some object introspection facilities. See section see Object library for information on classes and objects handling. Introspection facilities are, by default, available for all classes. However, in order to shrink the code size generation, it may be useful to disable class introspection. This decision can be taken on a per class basis (i.e., one class may be provided with introspection facilities while another one is not). The compiler option
-fno-reflection(see Chapter Compiler Description) prevents the compiler to generate the code required for introspecting the classes defined in the compiled module.
class-fields classbigloo procedureReturns the a description of the fields of class. This description is a list of field descriptions where each field description can be accessed by the means of the following library functions. The fields are those directly defined in class. That is
class-fieldsdoes not return fields defined in super classes of class.
class-all-fields classbigloo procedureReturns the a description of the fields of class. This description is a list of field descriptions where each field description can be accessed by the means of the following library functions. By contrast with
class-fields, this function returns fields that are also defined in the super classes of class. in th
find-class-field class symbolbigloo procedureReturns the field named symbol from class class. Returns
#fis such a field does not exist.
class-field? objbigloo procedureReturns #t if obj is a class field descriptor. Otherwise returns #f.
class-field-name fieldbigloo procedureReturns the name of the field. The name is a symbol.
class-field-accessor fieldbigloo procedureReturns a procedure of one argument. Applying this function to an object returns the value of the field described by field.
class-field-mutable? fieldbigloo procedureReturns
#tif the described field is mutable and
class-field-mutator fieldbigloo procedureReturns a procedure of two arguments. Applying this function to an object changes the value of the field described by field. It is an error to apply
class-field-mutatorto an immutable field.
class-field-info fieldbigloo procedureReturns the information associated to field (this the class declaration
.keepFor means of an example, here is a possible implementation of the
equal?test for objects:
(define (object-equal? obj1 obj2) (define (class-field-equal? fd) (let ((get-value (class-field-accessor fd))) (equal? (get-value obj1) (get-value obj2)))) (let ((class1 (object-class obj1)) (class2 (object-class obj2))) (cond ((not (eq? class1 class2)) #f) (else (let loop ((fields (class-fields class1)) (class class1)) (cond ((null? fields) (let ((super (class-super class))) (if (class? super) (loop (class-fields super) super) #t))) ((class-field-equal? (car fields)) (loop (cdr fields) class)) (else #f)))))))
class-creator classbigloo procedureReturns the creator for class. The creator is a function for which the arity depends on the number of slots the class provides (see Section see Creating and accessing objects). When an instance is allocated by the means of the
class-creator, as for direct instantiation, the class constructor is automatically invoked. Example:
(module foo (main main) (static (class c1 (c1-constructor)))) (define c1-constructor (let ((count 0)) (lambda (inst) (set! count (+ 1 count)) (print "creating instance: " count) inst))) (define (main argv) (let ((o1 (instantiate::c1)) (o2 (instantiate::c1)) (o3 ((class-creator c1)))) 'done)) ⇥ creating instance: 1 creating instance: 2 creating instance: 3
class-predicate classbigloo procedureReturns the predicate for class. This predicate returns
#twhen applied to object of type class. It returns