Objects and Entities categories
Objects, Classes and Slots
Classes
Free-able objects

Classes

Classes are organized into a tree, each class being the subclass of another one, called its superclass. This relation of being a subclass (inheritance) corresponds to set inclusion: each class denotes a subset of its superclass. So, in order to identify instances of a class as objects of its superclass, there has to be some correspondence between the structures of both classes: all slots of a class must be present in all its subclasses. Subclasses are said to inherit the structure (slots) of their superclass (while refining it with other slots). The root of the class tree is the class any since it is the set of all entities. Formally, a class is defined by its superclass and a list of additional slots. Two types of classes can be created: those whose instances will have a name and those whose instances will be unnamed. Named objects must inherit (not directly, but they must be descendents) of the class thing. A named object is an object that has a name, which is a symbol that is used to designate the object and to print it. A named object is usually created with the x :: C() syntax but can also be created with new(C, name).

Each slot is given as <name>:<range>=<default>. The range is a type and the optional default value is an object which type is included in <range>. The range must be defined before it is used, thus recursive class definitions use a forward definition principle (e.g., person).
 person <: thing // forward definition
 person <: thing(age:integer = 0father:person)
 woman <: person // another forward definition
 man <: person(wife:woman)
 woman <: person(husband:man)
 child <: person(school:string)
 complex <: object(re:floatim:float)
A class inherits from all the slots of its super-classes, so they need not be recalled in the definition of the class. For instance, here, the class child contains the slots age and father, because it inherited them from person.

A default value is used to place in the object slot during the instantiation (creation of a new instance) if no explicit value is supplied. The default value must belong to the range and will trigger rules or inverses in the same way an explicit value would. The only exception is the "unknown" value, which represents the absence of value. unknown is used when no default value is given (the default default value). Note that the default value is a real entity that is shared by all instances and not an expression that would be evaluated for each instantiation. The proper management of default values, or their absence through unknown, is a key feature of CLAIRE.

From a set-oriented perspective, a class is the set union of all the instances of its descendents (itself, its subclasses, the subclasses of its subclasses, etc.). In some cases, it may be useful to "freeze" the data representation at some point: for this, two mechanisms are offered: abstract and final. First, a class c can be declared to have no instances with abstract(c) such as in the following :
 abstract(person)
An abstract class is not an empty set, it contains the instances of its descendents. Second, a class can also be declared to have no more new descendents using final as follows :
 final(colors)
It is a good practice to declare final classes that are leaves in the class hierarchy and that are not meant to receive subclasses in the future. This will enable further optimizations from the compiler. A class can be declared to instantiate ephemeral objects, in which case its extension (the list of its instances) is not kept. An important consequence is that ephemeral objects may be garbage collected when they are no longer used. For this behavior, the class must be declared with ephemeral or inherit from ephemeral_object :
 action <: object(on:anyperformed_by:object)
 ephemeral(action)

 action <: ephemeral_object(on:anyperformed_by:object)
A class definition can be executed only once, even if it is left unchanged. On the other hand, CLAIRE supports the notion of a class forward definition. A forward definition contains no slots and no parentheses. It simply tells the position of the class in the class hierarchy. A forward definition must be followed by a complete definition (with the same parent class !) before the class can be instantiated. Attempts to instantiate a class that has been defined only with a forward definition will produce an error. A forward definition is necessary in the case of recursive class definitions. Here is a simple example :
 parent <: thing
 child <: thing(father:parent)
 parent <: thing(son:child)
Although the father of a child is a parent (in the previous example), creating an instance of child does not create an implicit instance of parent that would be stored in the father slot. Once an instance of child is created, it is your responsibility to fill out the relevant slots of the objects. There exists a way to perform this task automatically, using the close method. This method is the CLAIRE equivalent to the notion of a constructor(in a C++ or Java sense). CLAIRE does not support class constructors since its instantiation control structure may be seen as a generic constructor for all classes. However, there are cases when additional operations must be performed on a newly created object. To take this into account, the close method is called automatically when an instantiation is done if a relevant definition is found. Remember that the close method must always return the newly create object, since the result of the instantiation is the result of the close method. Here is an example that shows how to create a parent for each new child object :
 close(x:child-> (x.father := parent(), x)
Slots can be mono- or multi-valued. A multi-valued slot contains multiple values that are represented by a list (ordered) or a set (without duplicates). CLAIRE assumes by default that a slot with range list or set is multi-valued. However, the multi-valuation is defined at the property level. This is logical, since the difference between a mono-valued and a multi-valued slot only occurs when inversion or rules are concerned, which are both defined at the property level. This means that CLAIRE cannot accept slots for two classes with the same name and different multi-valuation status. For instance, the following program will cause an error :
 A <: thing(x:set[integer]) // forces CLAIRE to consider x as multi-valued
 B <: thing(x:stack[integer]) // conflict: x cannot be multi-valued
On the other hand, it is possible to explicitly tell CLAIRE that a slot with range list or set is mono-valued, as in the following correct example :
 A <: thing(x:set[integer])
 x.multivalued? := false // x is from A U B -> (set[integer] U stack[integer])
 B <: thing(x:stack[integer])
It is sometimes advisable to set up manually the multi-valuation status of the property before creating the slots, in order to make sure that this status cannot be forced by the creation of another class with a mono-valued slot with the same name (this could happen within a many-authors project who share a namespace). This is achieved simply by creating the property explicitly :
 x :: property(multivalued? = true// creates the property
 // ... whatever happens will not change x's multi-valuation
 B <: thing(x:set[integer]) // safe definition of a multi-valued slot


categories Classesnormal dispatch Kernel method

abstract(c:class) -> any

declares a class as an abtract class (without instances)


categories Classesnormal dispatch operationKernel method

add(self:property, x:object, y:any) -> void

add(self,x,y) is equivalent to self(x) :add y (This form is interesting when one wants to write such an expression for a variable self)


categories Classesnormal dispatch Core method

class!(x:type) -> class

class!(x) returns the intersection of all classes y such that x <= y that is the best class approximation (Such an intersection always exists since classes are organized in a lattice). Hence, if c is a class class!(c) = c.


categories Classesnormal dispatch Kernel interface

close :: property(open = 3)

close interface is used to define constructors. Each time a new object x is created close(x) is call. The only case where the close interface isn't called is when the object is created by the special mClaire/new! allocator. close should always return a valid object (its argument most of time).
 ACCOUNT_ID:integer := 0

 account <: ephemeral_object(account_id:integer)

 close(self:account: account ->
     (self.account_id := ACCOUNT_ID,
     ACCOUNT_ID :+ 1,
     self)


categories Classesnormal dispatch Kernel method

copy(x:object) -> object

copy(x) returns a duplicate of the object x. It is not recursive : slots of the copied object are shared with that of the original one.


categories Classesnormal dispatch operationKernel method

delete(self:property, x:object, y:any) -> any

delete(s, x) returns s if x is not in the list (resp. set) s without x otherwise.


categories Classesnormal dispatch Kernel method

ephemeral(self:class) -> any

declares a class as ephemeral: the member set is not maintained (ephemeral_object)


categories Classesnormal dispatch Core method

erase(self:property, x:object) -> any

erase(p, x) removes the value associated to x with the property p. The default value, or the unknown value, is placed in the slot x.p, and the inverse if updated (if any).


categories Classesnormal dispatch Kernel method

exception!() -> exception

exception!() returns the last exception that was raised.


categories Classesnormal dispatch Kernel method

final(c:class) -> any

declares a class as totally defined in the hierarchy: no new subclasses can be added.


categories Classesnormal dispatch Kernel method

get(self:property, x:object) -> any

get(self,x) is equivalent to self(x), but without any verification on unknown.


categories Classesnormal dispatch Kernel method

get(s:slot, x:object) -> any

get(s,x) returns the value of x associated with slot s


categories Classesnormal dispatch Kernel method

kill(self:object) -> any

kill(x) is used to remove an object from the database of the language. kill(x) does it properly, removing the object from all the relation network but without deallocating.


categories Classesnormal dispatch Kernel method

kill(self:class) -> any

kill(x) is used to remove an object from the database of the language. kill(x) does it properly, removing the object from all the relation network but without deallocating.


categories Classesnormal dispatch Core method

kill!(self:any) -> any

kill!(x) is more brutal than kill and deallocates without any checking.


categories Classesnormal dispatch Core method

known?(self:property, x:object) -> boolean

known?(p, x) is equivalent to get(p, x) != unknown


categories Classesnormal dispatch Core method

new(self:class) -> type[object glb member(self)]

new(self) creates an instance of the class self


categories Classesnormal dispatch Core method

new(self:class, %nom:symbol) -> type[thing glb member(self)]

new(self, %nom) creates a named instance of the class v (assumed to be a subclass of thing) with the name self


categories Classesnormal dispatch Kernel method

put(p:property U slot, x:object, y:any) -> any

put(p,x,y) is equivalent to p(x) := y but does not trigger the rules associated to p (seen as a relation) or the inverse of p. Besides, this operation is performed without any type-checking.


categories Classesnormal dispatch Core method

read(self:property, x:object) -> any

read(self,x) is strictly equivalent to self(x) : it reads the value and raises an exception if it is unknown.


categories Classesnormal dispatch Core method

unknown?(self:property, x:object) -> boolean

unknown?(p, x) is equivalent to get(p, x) = unknown


categories Classesnormal dispatch Core method

write(self:property, x:object, y:any) -> void

This method is used to store a value in a slot of an object. write(p, x, y) is equivalent to p(x) := y.