Objects and Entities | categories Objects, Classes and Slots Classes |
Free-able objects |
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 = 0, father:person) woman <: person // another forward definition man <: person(wife:woman) woman <: person(husband:man) child <: person(school:string) complex <: object(re:float, im:float) |
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) |
final(colors) |
action <: object(on:any, performed_by:object) ephemeral(action) action <: ephemeral_object(on:any, performed_by:object) |
parent <: thing child <: thing(father:parent) parent <: thing(son:child) |
close(x:child) -> (x.father := parent(), x) |
A <: thing(x:set[integer]) // forces CLAIRE to consider x as multi-valued B <: thing(x:stack[integer]) // conflict: x cannot be multi-valued |
A <: thing(x:set[integer]) x.multivalued? := false // x is from A U B -> (set[integer] U stack[integer]) B <: thing(x:stack[integer]) |
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 | Classes | normal dispatch | Kernel method |
declares a class as an abtract class (without instances)
categories | Classes | normal dispatch | operation | Kernel method |
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 | Classes | normal dispatch | Core method |
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 | Classes | normal dispatch | Kernel interface |
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 | Classes | normal dispatch | Kernel method |
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 | Classes | normal dispatch | operation | Kernel method |
delete(s, x) returns s if x is not in the list (resp. set) s without x otherwise.
categories | Classes | normal dispatch | Kernel method |
declares a class as ephemeral: the member set is not maintained (ephemeral_object)
categories | Classes | normal dispatch | Core method |
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 | Classes | normal dispatch | Kernel method |
exception!() returns the last exception that was raised.
categories | Classes | normal dispatch | Kernel method |
declares a class as totally defined in the hierarchy: no new subclasses can be added.
categories | Classes | normal dispatch | Kernel method |
get(self,x) is equivalent to self(x), but without any verification on unknown.
categories | Classes | normal dispatch | Kernel method |
get(s,x) returns the value of x associated with slot s
categories | Classes | normal dispatch | Kernel method |
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 | Classes | normal dispatch | Kernel method |
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 | Classes | normal dispatch | Core method |
kill!(x) is more brutal than kill and deallocates without any checking.
categories | Classes | normal dispatch | Core method |
known?(p, x) is equivalent to get(p, x) != unknown
categories | Classes | normal dispatch | Core method |
new(self) creates an instance of the class self
categories | Classes | normal dispatch | Core method |
new(self, %nom) creates a named instance of the class v (assumed to be a subclass of thing) with the name self
categories | Classes | normal dispatch | Kernel method |
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 | Classes | normal dispatch | Core method |
read(self,x) is strictly equivalent to self(x) : it reads the value and raises an exception if it is unknown.
categories | Classes | normal dispatch | Core method |
unknown?(p, x) is equivalent to get(p, x) = unknown
categories | Classes | normal dispatch | Core method |
This method is used to store a value in a slot of an object. write(p, x, y) is equivalent to p(x) := y.