# Classes

This chapter covers the class model and its associated operations. The class model is slightly different compared to traditional one. Because has dynamic symbol bindings, it is not necessary to declare the class data members. A class is an object which can be manipulated by itself. Such class is said to belongs to a group of meta class as described later in this chapter. Once the class concept has been detailed, the chapter moves to the concept of instance of that class and shows how instance data members and functions can be used. The chapter terminates with a description of dynamic class programming.

## Class object

A class object in the terminology is simply a nameset which can be replicated via a construction mechanism. A class is created with the reserved keyword `class`. The result is an object of type `Class` which supports various symbol binding operations.

### Class declaration and bindings

A new class is an object created with the reserved keyword `class`. Such class is an object which can be bound to a symbol.

const Color (class)

A list of initial instance data members can be specified as an argument to the `class` reserved keyword.

const Complex (class (re im))

Because a class acts like a nameset, it is possible to bind directly symbols with the qualified name notation.

const Color (class) const Color:RED-FACTOR 0.75 const Color:BLUE-FACTOR 0.75 const Color:GREEEN-FACTOR 0.75

When a data is defined in the class nameset, it is common to refer it as a static data member. A static data member is invariant over the instance of that class. When the data member is declared with the `const` reserved keyword, the symbol binding is `const` in the class nameset. It is also possible to use the `trans` reserved keyword.

### Class closure binding

A lambda or gamma expression can be define for a class. If the class do not reference an instance of that class, the resulting closure is called a static method of that class. Static methods are invariant among the class instances. The standard declaration syntax for a lambda or gamma expression is still valid with a class.

const Color:get-primary-from-string (color value) { trans val "0x" val:+= (switch color ( ("red" (value:substr 1 3)) ("green" (value:substr 3 5)) ("blue" (value:substr 5 7)) )) I Integer val }

The invocation of a static method is done with the standard qualified name notation.

Color:get-primary-from-string "red" "#23c4e5" Color:get-primary-from-string "green" "#23c4e5" Color:get-primary-from-string "blue" "#23c4e5"

### Class symbol access

A class acts as a nameset and therefore provides the mechanism to evaluate any symbol with the qualified name notation.

const Color:RED-VALUE "#ff0000" const Color:print-primary-colors (color) { println "red color " (Color:get-primary-color "red" color) println "green color " (Color:get-primary-color "green" color) println "blue color " (Color:get-primary-color "blue" color) }

# print the color components for the red color Color:print-primary-colors Color:RED-VALUE

## Instance

An instance of a class is an object which is constructed by a special class method called a constructor. If an instance constructor does not exist, the instance is said to have a default construction. An instance acts also as a nameset. The only difference with a class, is that a symbol resolution is done first in the instance nameset and then in the instance class. As a consequence, creating an instance is equivalent to define a default nameset hierarchy.

### Instance construction

By default, a instance of the class is an object which defines an instance nameset. The simplest way to define an anonymous instance is to create it directly.

const i ((class)) const Color (class) const red (Color)

The example above define an instance of an anonymous class. If a class object is bound to a symbol, such symbol can be used to create an instance of that class. When an instance is created, the special symbol named `this` is defined in the instance nameset. This symbol is bounded to the instance object and can be used to reference in an anonymous way the instance itself.

### Instance initialization

When an instance is created, the engine looks for a special lambda expression called `preset`. This lambda expression, if it exists, is executed after the default instance has been constructed. Such lambda expression is a method since it can refer to the `this` symbol and bind some instance symbols. The arguments which are passed during the instance construction are passed to the `preset` method.

const Color (class) trans Color:preset (red green blue) { const this:red (Integer red) const this:green (Integer green) const this:blue (Integer blue) } # create some default colors const Color:RED (Color 255 0 0) const Color:GREEN (Color 0 255 0) const Color:BLUE (Color 0 0 255) const Color:BLACK (Color 0 0 0) const Color:WHITE (Color 255 255 255)

In the example above, each time a color is created, a new instance object is created. The constructor is invoked with the `this` symbol bound to the newly created instance. Note that the qualified name `this:red` defines a new symbol in the instance nameset. Such symbol is sometimes referred as an instance data member. Note as well that there is no ambiguity in resolving the symbol `red`. Once the symbol is created, it shadows the one defined as a constructor argument.

### Initialization with data member list

If the class was defined with a list of data members, the instance is created with these data members initialized to nil. Each symbol is defined as a transient symbol since they are supposed to be modified later. As a consequence, it is possible to use the reserved keyword `trans` inside the `preset` method.

const Complex (class (re im)) trans Complex:preset (re im) { trans this:re (Real re) trans this:im (Real im) }

The use of a class data member list is primarily dictated by the existence of a copy constructor for that class. If a method try to construct an object, an evaluation of an unbound data member with `trans` might trigger an inner instance data member to be set instead of the real one. This behavior exists only with `trans`. When `const` is used, the implementation guarantee that the symbol binding will be local to that instance.

### Instance symbol access

An instance acts as a nameset. It is therefore possible to bind locally to an instance a symbol. When a symbol needs to be evaluated, the instance nameset is searched first. If the symbol is not found, the class nameset is searched. When an instance symbol and a class symbol have the same name, the instance symbol is said to shadow the class symbol. The simple example below illustrates this property.

const c (class) const c:a 1 const i (c) const j (c) const i:a 2 # class symbol access println c:a # shadow symbol access println i:a # non shadow access println j:a

When the instance is created, the special symbol `meta` is bound in the instance nameset with the instance class object. This symbol can therefore be used to access a shadow symbol.

const c (class) const i (c) const c:a 1 const i:a 2 println i:a println i:meta:a

The symbol `meta` must be used carefully, especially inside an initializer since it might create an infinite recursion as shown below.

const c (class) trans c:preset nil (const i (this:meta)) const i (c)

### Instance method

When lambda expression is defined within the class or the instance nameset, that lambda expression is callable from the instance itself. If the lambda expression uses the `this` symbol, that lambda is called an instance method since the symbol `this` is defined in the instance nameset. If the instance method is defined in the class nameset, the instance method is said to be global, that is, callable by any instance of that class. If the method is defined in the instance nameset, that method is said to be local and is callable by the instance only. Due to the nature of the nameset parent binding, only lambda expression can be used. Gamma expressions will not work since the gamma nameset has always the top level nameset as its parent one.

const Color (class) # class constructor trans Color:preset (red green blue) { const this:red (Integer red) const this:green (Integer green) const this:blue (Integer blue) } const Color:RF 0.75 const Color:GF 0.75 const Color:BF 0.75 # this method returns a darker color trans Color:darker nil { trans lr (Integer (max (this:red:* Color:RF) 0)) trans lg (Integer (max (this:green:* Color:GF) 0)) trans lb (Integer (max (this:blue:* Color:BF) 0)) Color lr lg lb } # get a darker color than yellow const yellow (Color 255 255 0) const dark-yellow (yellow:darker)

### Instance operators

Any operator can be defined at the class or the instance level. Operators like `==` or `!=` generally requires the ability to assert if the argument is of the same type of the instance. The global operator `==` will return true if two classes are the same. With the use of the `meta` symbol, it is possible to assert such equality.

# this method checks that two colors are equals trans Color:== (color) { if (== Color color:meta) { if (!= this:red color:red) (return false) if (!= this:green color:green) (return false) if (!= this:blue color:blue) (return false) eval true } false } # create a new yellow color const yellow (Color 255 255 0) (yellow:== (Color 255 255 0)) # true

The global operator `==` returns `true` if both arguments are the same, even for classes. Method operators are left open to the user.

### Complex number example

As a final example, a class simulating the behavior of a complex number is given hereafter. The interesting point to note is the use of the operators. As illustrated before, the class uses uses a default method method to initialize the data members.

# class declaration const Complex (class (re im))

# constructor initializer trans Complex:preset (re im) { trans this:re (Real re) trans this:im (Real im) }

# class mutators trans Complex:set-re (x) (trans this:re re) trans Complex:set-im (x) (trans this:im im)

# class accessors trans Complex:get-re nil (Real this:re) trans Complex:get-im nil (Real this:im) trans Complex:module nil { trans result (Real (+ (* this:re this:re) (* this:im this:im))) result:sqrt } trans Complex:format nil { trans result (String this:re) result:+= "+i" result:+= (String this:im) }

# complex predicate const complex-p (c) ( if (instance-p c) (== Complex c:meta) false)

# operators trans Complex:== (c) ( if (complex-p c) (and (this:re:== c:re) (this:im:== c:im)) ( if (number-p c) (and (this:re:== c) (this:im:zero-p)) false))

trans Complex:= (c) { if (complex-p c) { this:re:= (Real c:re) this:im:= (Real c:im) return this } this:re:= (Real c) this:im:= 0.0 return this }

trans Complex:+ (c) { trans result (Complex this:re this:im) if (complex-p c) { result:re:+= c:re result:im:+= c:im return result } result:re:+= (Real c) eval result }

## Inheritance

Inheritance is the mechanism by which a class or an instance inherits methods and data member access from a parent object. The class model is based on a single inheritance model. When an instance object defines a parent object, such object is called a super instance. The instance which has a super instance is called a derived instance. The main utilization of inheritance is the ability to reuse methods for that super instance.

### Derivation construction

A derived object is generally defined within the `preset` method of that instance by setting the `super` data member. The `super` reserved keyword is set to nil at the instance construction. The good news is that any object can be defined as a super instance, including builtin object.

const c (class) const c:preset nil { trans this:super 0 }

In the example above, an instance of class `c` is constructed. The super instance is with an integer object. As a consequence, the instance is derived from the `Integer` instance. Another consequence of this scheme is that derived instance do not have to be built from the same base class.

### Derived symbol access

When an instance is derived from another one, any symbol which belongs to the super instance can be access with the use of the `super` data member. If the super class can evaluate a symbol, that symbol is resolved automatically by the derived instance.

const c (class) const i (c) trans i:a 1 const j (c) trans j:super i println j:a

When a symbol is evaluated, a set of search rules is applied. The engine gives the priority to the class nameset vs the super instance. As a consequence, a static data member might shadow a super instance data member. The rule associated with a symbol evaluation can be summarized as follow.

Look in the instance nameset.
Look in the class nameset.
Look in the super instance if it exists.
Look in the base object.

### Instance re-parenting

The ability to set dynamically the parent instance make the object model an ideal candidate to support instance re-parenting. In this model, a change in the parent instance is automatically reflected at the instance method call.

const c (class) const i (c) trans i:super 0 println (i:to-string) # 0 trans i:super "hello world" println (i:to-string) # hello world

In this example, the instance is originally set with an `Integer` instance parent. Then the instance is re-parented with a `String` instance parent. The call to the `to-string` method illustrates this behavior.

### Instance re-binding

The ability to set dynamically the instance class is another powerful feature of the object model. In this approach, the instance meta class can be changed dynamically with the `mute` method. Furthermore, it is also possible to create initially an instance without any class binding, which is later muted.

# create a point class const point (class) # point class initializer trans point:preset (x y) { trans this:x x trans this:y y }

# create an empty instance const p (Instance) # bind the point class p:mute point 1 2

In this example, when the instance is muted, the `preset` method is called automatically with the extra arguments.

### Instance inference

The ability to instantiate dynamically inferred instance is offered by the object model. An instance b is said to be inferred by the instance a when the instance a is the super instance of the instance b. The instance inference is obtained by binding the `infer` symbol to a class. When an instance of that class is created, the inferred instance is also created.

# base class A const A (class) # inferred class B const B (class) const A:infer B

# create an instance from A const x (A) assert B (x:meta) assert A (x:super:meta)

In this example, when the instance is created, the inferred instance is also created and returned by the instantiation process. The `preset` method is only called for the inferred instance if possible or the base instance if there is no inferring class. Because the base `preset` preset method is not called automatically, the inferred method is responsible to do such call.

trans B:preset (x y) { trans this:xb x trans this:yb y if (== A this:super:meta) (this:super:preset x y) }

Because the class can mute from one call to another and also the inferred class, the `preset` method call must be used after a discrimination of the meta class has been made as indicated by the above example.