Object Systems available in SchemeSpheres

define-structure (native in Gambit)

Summary of the object system

Syntax and procedures

(define-structure type-name <field>...)

The define-structure expands into a set of definitions of the following procedures:

Record data types have a printed representation that includes the name of the type and the name and value of each field. Record data types can not be read by the read procedure.

Examples

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Creates the type point2d and the following procedures:

define-record-type (native implementation of SRFI-9 in Gambit)

Summary of the object system

Syntax and procedures

<record-type-definition> = (define-record-type
                            <type-name>
                            [ <constructor> <predicate> ]
                            <field> ...)

<constructor> = <constructor-name>
              | (<constructor-name> <field-tag> ...)

<predicate> = identifier

<field> = <field-tag>
        | (<field-tag>)
        | (<field-tag> <accessor-name>)
        | (<field-tag> <accessor-name> <modifier-name>)

<field-tag> = identifier
<*-name> = identifier

An instance of define-record-type is equivalent to the following definitions:

Please, refer to the SRFI-9 document for more information.

Examples

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Creates the type :point2d and the following procedures:

define-type (native in Gambit)

Summary of the object system

Syntax and procedures

<type-definition> = (define-type <type-name> [<attribute> ...] <field> ...)

<attribute> = id: <uuid>
            | constructor: <constructor-name>
            | constant-constructor: <constant-constructor-name>
            | predicate: <predicate-name>
            | extender: <extender-name>
            | prefix: <prefix-name>
            | macros:
            | type-exhibitor: <type-exhibitor-name>
            | implementer: <implementer-name>
            | opaque:
            | <field-attribute>

<field> = identifier
        | <field-attribute> ... <field> ...
        | (identifier [<accessor-name> [<modifier-name>]])
        | (identifier <field-attribute> ...)

<field-attribute> = read-write:
                  | read-only:
                  | equality-test:
                  | equality-skip:
                  | printable:
                  | unprintable:
                  | init: <expr>

<*-name> = identifier

Setting this option will allow you seeing generated code for define-type:

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Attributes:

Field attributes:

All field attributes except init: can be specified at define-type level and will affect all fields. A field can specify its own field attributes to override default attributes. Field may have non-conventional getter and setter names, specified after field name in that order. The attributes can be specified inside the field definition or can prefix a field definition (in which case they apply to all fields from that point):

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

is equivalent to

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Non-generative type definitions are obtained by explicitly specifying a "globally unique identifier" (GUID) for the type. This way, even different programs which use the same type definition can agree on the identity of that type. The type descriptor is not stored in a variable because is is now constant and can be denoted with a literal here needed (i.e. the expression '#). The type-id is also known and used directly in the definition of point?. A "constant constructor" is also defined (as a macro), so that constants can be created. All evaluations of a given constant constructor yield the same (eq?) object. The arguments of the constant constructor must be self-evaluating or quoted constants.

Instances of non-generative types can be communicated (write then read) by dumping all their fields, including the type descriptor. Here is an example for the object (make-point 44 55):

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Most of the explanations are taken from this document, which you can use to further clarification.

Examples

Attributes

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Inheritance

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Combine custom field accessor/modifier with an initializer

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

The opaque: flag

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

SRFI-99 Records (object: record)

Summary of the object system

These system is implemented as a module in SchemeSpheres (object: record), and uses the low-level facilities of Gambit for defining record types. These are the main features that brings to the native Gambit systems:

Most of the work for bringing SRFI-99 to Gambit has been done by Arthur T Smyles.

Syntax and procedures

This is the syntax brought by the syntactic layer, but is not necessary to use it (you can generate the procedures manually via the generators).

<record-type-definition> = (define-record-type <type-spec>
                            <constructor-spec>
                            <predicate-spec>
                            <field-spec> ...)

<type-spec> = <type-name>
            | (<type-name> <parent>)

<constructor-spec> = #f
                   | #t
                   | <constructor-name>
                   | (<constructor-name> <field-name> ...)

<predicate-spec> = #f
                 | #t
                 | <predicate-name>

<field-spec> = <field-name>
             | (<field-name>)
             | (<field-name> <accessor-name>)
             | (<field-name> <accessor-name> <mutator-name>)

<parent> = <expression>

<*-name> = identifier

These are the procedures in the procedural layer (rtd stands for record type descriptor):

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Finally, these are the procedures in the inspection layer:

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

For more information on the standard API to manipulate the generated records as well as manually generate procedures, please refer to SRFI-99 documentation.

Examples

Using the procedure generators and shadowing parent's fields

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Custom constructor with the constructor generator

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Type-classes (object: type-class)

Summary of the object system

In essence, type-classes define a set of functions. Then, using this "interface", other functions can be defined (called type-class polymorphic functions), similar to multimethods. Type-classes are parameterized types, acting like "types of types" in a sense: they allow you to describe the constraints a type should obbey (type-class constraints), and any instance of the typeclass (a type) must conform with them. In other words, type-classes correspond to the set of types which have certain operations defined for them, and type class polymorphic functions work only for types which are instances of the type class(es) in question.

Type-classes can be used with records. They are similar to interfaces in OOP. Both define a set of types which implement a specified list of operations. However, there are a couple of important ways in which type classes are more general:

While the previous object systems are focused on defining structures made of compound data (records), type-classes are focused on the functions defined for these structures. However, both type-classes and records can be combined to model more traditional OOP and extend its capabilities through the parameterized types.

The type-classes module can be summarized as follows:

The layer of type-classes adds an indirection to funtion calls: all functions are wrapped by extra lambdas.

Syntax and procedures

(define-class <field-form> ...)

(define=> (<procedure-name> <class-form> ...) . body)

(lambda=> (<class-form> ...) . body)

(with (<instance-form> ...) . body)

(import-instance <instance-form> ...)

<field-form> = field-label
             | (<superclass-name> field-label)

<class-form> = <class-name>
             | (<class-name> <prefix-symbol>)

<instance-form> = (<class-name> <instance-expr>)
                | (<class-name> <instance-expr> <prefix-symbol>)

Further explanation

This is an extract from the code and explanations by the original author (André Van Tonder), with some modifications to adapt it to SchemeSpheres:

Type classes provide a level of abstraction superior to CLOS-style generic functions in certain respects. For example, a collection abstraction:

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

cannot be neatly expressed with OO or generic functions. The problem is the signature of empty -- generic functions cannot dispatch on the expected result type. The same is true for return in the signature

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Unfortunately type classes rely on static type inference to resolve these ambiguities, which makes their implementation problematic in Scheme. However, they can be simulated by "dictionary passing" as happens under the hood in Haskell. Although in its raw form, this technique is very burdensome to the programmer, it can be substantially alleviated by a few macros.

In compensation, since the instances (dictionaries) of "type classes" are now first class, the Scheme programmer gets a much more powerful abstraction tool. Indeed, Haskell type classes have some serious shortcomings, making them unsuitable for expressing many very natural abstractions. For example, the integers form a monoid under addition, and also under multiplication. In Haskell, the integers can only be an instance of a monoid class in one way. This problem does not arise with first class instances. Consider also

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

This cannot be expressed in Haskell, since the type of dimension would be ambiguous. Also, it is impossible to express common operations such as taking the direct sum or tensor product of two vector spaces. Again, this is no problem with first class instances as defined below.

Here is a short tutorial:

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Examples

Object-orientation example, combining with simple records

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

More complex types

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Extensible interpreter

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio

Prototypes (object: prototype)

Summary of the object system

Prototypes are a different approach to object modeling, based on the Self/Javascript model. It can be used instead of records and type-classes. In this approach, behaviour reuse (inheritance) is performed cloning existing objects that serve as prototypes. The language feature that supports prototype-based programming is called delegation. This is the process in which the right function is selected to be dispatched for a given message passed to an object.

Objects in prototype-based programming can encapsulate both data and functionality, and serve as prototypes for other objects. The runtime of the system takes care of dispatching the correct method or finding the right piece of data simply by following a series of delegation pointers (from object to its prototype) until a match is found.

In a prototype-based object system, an object is just a set of slots. A slot has a name and a value, or a handler procedure which reacts on messages associated with the slot. Some slots are special, so-called parent slots, whose use will become apparent shortly.

Objects receive messages, consisting of a selector (a symbol), and zero or more arguments. When an object receives a message, the object searches for a slot whose name is equal (tested with eq?) to the message selector. When it finds such a slot, it invokes the slot's handler, or returnes the slot's value, as appropriate. When the slot is not in the object, all objects in parent slots are queried for that slot. That is the process mentioned earlier called delegation.

An object is created by cloning an existing object. The new object is empty except for a single parent slot, which points to the cloned object. This way, the new object behaves exactly like the old one until its particular behaviour or data are changed by the programmer.

The main features of this system are:

The prototypes system is a port of the project called TinyTalk, by Kenneth A Dicke.

Syntax and procedures

($ <message> <object> <arguments>) -- send a message to an object
(<< <object> <message> <arguments>) -- alternate syntax

(object (<field-spec> ...) <method-spec> ...) -- produces an object instance
(prototype-object? thing) -- universal predicate
(define-predicate <pred?>) -- defines a universal predicate
(string<< thing) -- returns a string describing thing


($ field-names <obj>) -- names of setter/getter field value access
($ shallow-clone <obj>) -- shallow-clone object (does *not* clone delegate(s))
($ deep-clone <obj>) -- deep-clone object (does clone delegate(s))
($ add-method! <obj> <name-sym> <proc>)
($ remove-method! <obj> <name-sym>)
($ methods-alist <obj>) -- all (name . method) pairs
($ lookup <obj>) -- symbol-> method or #f [single level (no delegate)]

($ ->string <obj>) -- descriptive string (should be implemented by user)

Examples

This is a tutorial by the original author, Kenneth A Dickey (edited):

Support for Python 2.5 has turned off. Please refer to https://goo.gl/aESk5L for more informatio