claire v3.3.39 documentation

Documentation generated by XL CLAIRE v3.3.39 at Thu, 21 Dec 2006

Copyright

Preamble

This documentation is mainly issued from the document by Yves Caseau "Introduction to the CLAIRE Programming Language version 3.3". It has been included in the source files of XL CLAIRE such to be used with the XL code documentation generator. In this documentation XL specific stuffs are denoted by an [XL] mark.

Category index

  1. Introduction
    1. What is CLAIRE ?
    2. Design
    3. Features
    4. Inspirations
  2. Primitives
    1. Integers and Floats
    2. Dates and Times [XL]
    3. Chars
    4. Strings
  3. Objects, Classes and Slots
    1. Objects and Entities
    2. Classes
    3. Free-able objects [XL]
    4. parametric class
    5. Calls and Slot Access
    6. Updates
  4. Lists, Sets and Instructions
    1. Lists, Sets and Tuples
    2. Blocks
    3. Conditionals
    4. Loops
    5. Instantiation
    6. Exception Handling
    7. array
  5. Methods and Types
    1. Methods
    2. Types
    3. Polymorphism
    4. Escaping Types
    5. Selectors, Properties and Operations
    6. Iterations
  6. Tables, Rules and Hypothetical Reasoning
    1. Tables
    2. Rules
    3. Hypothetical Reasoning
  7. I/O, Modules and System Interface
    1. Communication ports [XL]
    2. Printing
    3. WCL syntax [XL]
    4. Reading
    5. Symbols
    6. Modules
    7. Global Variables and Constants
    8. Command line handling [XL]
    9. Serialization [XL]
  8. Platform
    1. Miscellaneous
    2. Environment variables [XL]
    3. Process handling [XL]
    4. File system [XL]
    5. Signal Handling [XL]
  9. Using XL Claire
    1. Debugging [XL]
  10. driving optimizations

Command line option index

  1. Command line help : {-h | -help} +[<m> | <option> | <index>]
  2. About box : -about
  3. Memory initialization : [-auto-s <main> <world>
  4. Verbose : -v [<m><level>
  5. Exit now : -q [<exitcode>]
  6. Early termination : -qonerror | -errassegv
  7. Avoid banner : -nologo
  8. Avoid editline : -noel
  9. Terminal color : -color | -nocolor
  10. Trace file : -trace [a | append] <file>
  11. Sampling Cmemory : -sample <period>
  12. Change directory : -chdir <dir>
  13. Debugger : -debug
  14. Load file : {-f | -ef} +[<file>]
  15. Eval expression : {-princ | -print | -eval<exp>
  16. Load script : {-x | -xe | -x<S>-<W> | -xe<S>-<W><file> [<args>]
  17. Goto definition : -gotodef [<dir>] [<s>]
  18. Generate documentation : [-doclink] [-onefile | -categories] {-apidoc | -codedoc<m>
  19. No ffor construct : -noffor
  20. Compiler environment : -env <env>
  21. Link with module : -m <m>[/<version>] | -l <library>
  22. Output directory : -od <directory>
  23. Safety : -safe | -os <safety>
  24. Output name : -o <name>
  25. Optimization : -D | -O
  26. Profiler : -p
  27. C++ compiler : -cpp <cxxoption> | -link <linkeroption> | -make <makeroption>
  28. Compile module : {-cc | -cl | -cm} [<m>]
  29. Compile module library : [-both] {-cls | -call [-sm <m>] [-em <m>]}
  30. Module publication : [-sudo] [-ov] {-publish | -export [<i> | <directory>]}
  31. New module : -nm [<partof>/]<name> +[<m> | <f>{.cpp | .cl | .h}]
  32. Module info : -ml | {-mi <m>[/<version>]}
  33. Configuration file : -cx <test>
  34. No init : -n
  35. Fast dispatch : -fcall
  36. Console : -noConsole | -wclConsole

Global Variable index

Interface index

Class index

Method index


claire categories


categories
Introduction
What is CLAIRE ?
Design

Introduction

What is CLAIRE ?

CLAIRE is a high-level, portable, functional and object-oriented language with advanced rule processing capabilities. It is intended to allow the programmer to express complex algorithms with fewer lines and in an elegant and readable manner.

To provide a high degree of expressivity, CLAIRE uses :

To achieve its goal of readability, CLAIRE uses :


What is CLAIRE ? categories
Introduction
Design
Features

Design

CLAIRE was designed for advanced applications that involve complex data modeling, rule processing and problem solving. CLAIRE was meant to be used in a C++ environment, either as a satellite (linking CLAIRE programs to C++ programs is straightforward) or as an upper layer (importing C++ programs is also easy). The key set of features that distinguishes CLAIRE from other programming languages has been dictated by our experience in solving complex optimization problems. Of particular interest are two features that distinguish CLAIRE from procedural languages such as C++ or Java :


Design categories
Introduction
Features
Inspirations

Features

CLAIRE provides automatic memory allocation/de-allocation, which would have prevented an easy implementation as a C++ library. Also, set-oriented programming is much easier with a set-oriented language like CLAIRE than with libraries. CLAIRE is about ten years old and the current version reaches a new level of maturity.

CLAIRE is a high-level language that can be used as a complete development language, since it is a general purpose language, but also as a pre-processor to C++ or Java, since a CLAIRE program can be naturally translated into a C++ program (We continue to use C++ as our target language of choice, but the reader may now substitute Java to C++ in the rest of this document). CLAIRE is a set-oriented language in the sense that sets are first-class objects, typing is based on sets and control structures for manipulating sets are parts of the language kernel. Similarly, CLAIRE makes manipulating lists easy since lists are also first-class objects. Sets and lists may be typed to provide a more robust and expressive framework. CLAIRE can also be seen as a functional programming language, with full support for lambda abstraction, where functions can be passed as parameters and returned as values, and with powerful parametric polymorphism.

CLAIRE is an object-oriented language with single inheritance. As in SMALLTALK, everything that exists in CLAIRE is an object. Each object belongs to a unique class and has a unique identity. Classes are the corner stones of the language, from which methods (procedures), slots and tables (relations) are defined. Classes belong themselves to a single inheritance hierarchy. However, classes may be grouped using set union operators, and these unions may be used in most places where a class would be used, which offers an alternative to multiple inheritance. In a way similar to Modula-3, CLAIRE is a modular language that provides recursively embedded modules with associated namespaces. Module decomposition can either be parallel to the class organization (mimicking C++ encapsulation) or orthogonal (e.g., encapsulating one service among multiple classes).

CLAIRE is a typed language, with full inclusion polymorphism. This implies that one can use CLAIRE with a variety of type disciplines ranging from weak typing in a manner that is close to SMALLTALK up to a more rigid manner close to C++. This flexibility is useful to capture programming styles ranging from prototyping to production code development. The more typing information available, the more CLAIRE's compiler will behave like a statically typed language compiler. This is achieved with a rich type system, based on sets, that goes beyond types in C++. This type system provides functional types (second-order types) similar to ML, parametric types associated to parametric classes and many useful type constructors such as unions or intervals. Therefore, the same type system supports the naive user who simply wishes to use classes as types and the utility library developer who needs a powerful interface description language.

[XL] Starting with XL CLAIRE, CLAIRE is intended to cover various aspects of web oriented application (running in a CGI like environment), that is to serve dynamic content over the web. The Wcl syntax is introduced as new method of printing, in a similar way to printf but closer to the HTML syntax. The development of a web oriented agent would however require the module Wcl, not included in the standard XL CLAIRE distribution.


Features categories
Introduction
Inspirations
Primitives
Integers and Floats

Inspirations

As the reader will notice, CLAIRE draws its inspiration from a large number of existing languages. A non-exhaustive list would include SMALLTALK for the object-oriented aspects, SETL for the set programming aspects, OPS5 for the production rules, LISP for the reflection and the functional programming aspects, ML for the polymorphism and C for the general programming philosophy. As far as its ancestors are concerned, CLAIRE is very much influenced by LORE, a language developed in the mid 80s for knowledge representation. It was also influenced by LAURE but is much smaller and does not retain the original features of LAURE such as constraints or deductive rules. CLAIRE is also closer to C in its spirit and its syntax than LAURE was.


Introduction
Inspirations
categories
Primitives
Integers and Floats
Dates and Times

Primitives

Integers and Floats

Both floats and integers are CLAIRE primitives. integers are represented using 30 bits (which is required for the OID model) and are always signed. Floats are represented as C double precision floating point numbers.

Arithmetic between integers and float can be handled using conversion method integer! and float! :
 1 + 2 -> 3
 1 + 2. -> error
 1 + integer!(2.-> 3
 float!(1+ 2. -> 3.


Integers and Floats categories
Primitives
Dates and Times
Chars

Dates and Times [XL]

Dates and times are represented using floats containing an UNIX C time that is the time in seconds since the Epoch (00:00:00 UTC, January 1, 1970). The use of float is required since CLAIRE integer are coded on 30 bits and times on 32 bits.

Internally, CLAIRE always handles dates represented in UTC. Times are referenced on the Epoch such the arithmetic between date and time can be made with the standard float arithmetic.

Two kind of timer are also supported, one that account time in the process time (time_set/time_get) and one that count in real time (timer!/elapsed).


Dates and Times categories
Primitives
Chars
Strings

Chars

In CLAIRE chars are true object (i.e. not primitive) that hold an 8 bit value, one can obtain this value with integer!(c:char) and make a char from an integer i with char!(i:integer).

[XL] Starting with XL CLAIRE the internal 8 bit value is stored as an unsigned char (vs. signed char in CLAIRE 3) such the composition char!(integer!()) is actually the identity for all possible existing char in the system. If their is no char that hold a given value n then char!(n) will produce an error.


Chars categories
Primitives
Strings
Objects, Classes and Slots
Objects and Entities

Strings

In CLAIRE strings are represented using a C char*, that is a sequence of 8 bit characters. The length of a string is computed by the general method length @ string :
 length("toto"-> 4
[XL] Starting with XL CLAIRE strings may contain null char under certain restriction. Indeed XL CLAIRE makes difference between strings that are dynamically allocated and strings that are are statically compiled :
 s0 :: "a static string" // would compile as a static string
 s1 :: ("a string" /+ string!('\0') /+ "another string"// dynamically built string
In the above example s0 would be compiled statically, that is the string content would be allocated outside claire memory (e.g. in the data space of the program). In addition s1 is allocated dynamically and stored in a chunk of the claire memory. The important difference between these two string is the handling of their length :

Notice that inside the interpreter all strings are dynamically allocated which can give different behavior between the interpreted and the compiled code, for instance :
 s :: "a\0b" // dangerous !!! static string that contain a null
 (printf("~S\n"length(s)))
In the interpreter this code would print 3 whereas the compiled code would print 1. Indeed s would be compiled statically (outside claire memory) and its length would handled by strlen.

As a general rule, any method that returns a new string returns a dynamic string. This is especially true for the port interface (fread and friends) which is particularly convenient to handle binary streams without having to deal with the null.


Primitives
Strings
categories
Objects, Classes and Slots
Objects and Entities
Classes

Objects, Classes and Slots

Objects and Entities

A program in CLAIRE is a collection of entities (everything in CLAIRE is an entity). Some entities are pre-defined, we call them primitive entities, and some others may be created when writing a program, we call them objects. The set (a class) of all entities is called any and the set (a class also) of all objects is called object.

[XL] In XL CLAIRE port are not imported entity but implemented an extensible class vs. primitive (see port).

Primitive entities consist of integers, floats, symbols, strings and functions. The most common operations on them are already built in, but you can add yours. You may also add your own entity classes using the import mechanism.

Objects can be seen as "records", with named fields (called slots) and unique identifiers. Two objects are distinct even if they represent the same record. The data record structure and the associated slot names are represented by a class. An object is uniquely an instance of a class, which describes the record structure (ordered list of slots). CLAIRE comes with a collection of structures (classes) as well as with a collection of objects (instances).

Definition : A class is a generator of objects, which are called its instances. Classes are organized into an inclusion hierarchy (a tree), so a class can also be seen as an extensible set of objects, which is the set of instances of the class itself and all its subclasses. A class has one unique father in the inclusion hierarchy (also called the inheritance hierarchy), called its superclass. It is a subclass of its superclass.

Each entity in CLAIRE belongs to a special class called its owner, which is the smallest class to which the entity belongs. The owner relationship is the extension to any of the traditional isa relationship between objects and classes, which implies that for any object x, x.isa = owner(x).

Thus the focus on entities in CLAIRE can be summarized as follows: everything is an entity, but not everything is an object. An entity is described by its owner class, like an object, but objects are "instantiated" from their classes and new instances can be made, while entities are (virtually) already there and their associated (primitive) classes don't need to be instantiated. A corollary is that the list of instances for a primitive class is never available.


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


Classes categories
Objects, Classes and Slots
Free-able objects
parametric class

Free-able objects [XL]

Starting with XL CLAIRE, a class can inherit from freeable_object that are special kind of ephemeral objects regarding the Garbage collector (GC). We can define two GC callbacks (prefree! and free!) for such object that will be called when the GC attempt to free an object giving a chance for such object to perform a cleanup operation :

As an illustration we could define the following long_double class that import from C++ the 'long double' data type :

 long_double* <: import()
 long_double <: freeable_object(value:long_double*)
 (c_interface(long_double*"long double*"))

 close(self:long_double: long_double ->
     (self.value := externC("(long double*)::malloc(sizeof(long double))"long_double*),
     self)

 free!(self:long_double: void -> externC("::free(self->value)")


Free-able objects categories
Objects, Classes and Slots
parametric class
Calls and Slot Access

parametric class

A class can be parameterized by a subset of its slots. This means that subsets of the class that are defined by the value of their parameters can be used as types. This feature is useful to describe parallel structures that only differ by a few points: parametrization helps describing the common kernel, provides a unified treatment and avoids redundancy.

A parameterized class is defined by giving the list of slot names into brackets. Parameters can be inherited slots, and include necessarily inherited parameters.
 stack[of<: object(of:type,
                     content:list[any],
                     index:integer = 0)
 complex[re,im<: object(re:float = 0.0im:float = 0.0)
CLAIRE includes a type system that contains parametric class selections. For instance, the set of real numbers can be defined as a subset of complex with the additional constraint that the imaginary part is 0.0. This is expressed in CLAIRE as follows :
 complex[re:floatim:{0.0}]
In the previous example with stacks, parametric sub-types can be used to designate typed stacks. We can either specify the precise range of the stack (i.e., the value of the of parameter) or say that the range must be a sub-type of another type. For instance, the set of stacks with range integer and the set of stacks which contain integers are respectively :
 stack[of:{integer}]
 stack[of:subtype[integer]]


parametric class categories
Objects, Classes and Slots
Calls and Slot Access
Updates

Calls and Slot Access

Calls are the basic building blocks of a CLAIRE program. A call is a polymorphic function call (a message) with the usual syntax : a selector followed by a list of arguments between parentheses. A call is used to invoke a method.

When the selector is an operation, such as +, -, %, etc... (% denotes set membership) an infix syntax is allowed (with explicit precedence rules) :
 eval(x), f(x,y,z), x.pricey.name
If a slot is read before being defined (its value being unknown), an error is raised. This only occurs if the default value is unknown. To read a slot that may not be defined, one must use the get(r:property,x:object) method :
 John.father // may provoke an error if John.father is unknown
 get(father,john// may return unknown
When the selector is an operation, such as +,-,%,etc... (% denotes set membership) an infix syntax is allowed (with explicit precedence rules). Hence the following expressions are valid :
 1 + 2
 1 + 2 * 3
Note that new operations may be defined. This syntax extends to boolean operations (and:& and or:|). However, the evaluation follows the usual semantic for boolean expression (e.g., (x & y) does not evaluate y if x evaluates to false) :
 (x = 1& ((y = 2| (y > 2)) & (z = 3)
The values that are combined with and/or do not need to be boolean values (although boolean expressions always return the boolean values true or false). Following a philosophy borrowed from LISP, all values are assimilated to true, except for false, empty lists and empty sets. The special treatment for the empty lists and the empty sets yields a simpler programming style when dealing with lists or sets. Notice that in CLAIRE 3.0, contrary to previous releases, there are many empty lists since empty lists can be typed (list<integer>(), list<string>(), ... are all different).

A dynamic functional call where the selector is evaluated can be obtained using the call method. For instance, call(+,1,2) is equivalent to +(1,2) and call(show,x) is equivalent to show(x). The difference is that the first parameter to call can be any expression. This is the key for writing parametric methods using the inline capabilities of CLAIRE. This also means that using call is not a safe way to force dynamic binding, this should be done using the property abstract. An abstract property is a property that can be re-defined at any time and, therefore, relies on dynamic binding. Notice that call takes a variable number of arguments. A similar method named apply can be used to apply a property to an explicit list of arguments.

Since the use of call is somehow tedious, CLAIRE supports the use of variables (local or global) as selectors in a function call and re-introduce the call implicitly. For instance :
 compose(f:propertyg:propertyx:any: any
     => f(g(x))
is equivalent to :
 compose(f:propertyg:propertyx:any)
     => call(fcall(g,x))


Calls and Slot Access categories
Objects, Classes and Slots
Updates
Lists, Sets and Instructions
Lists, Sets and Tuples

Updates

Assigning a value to a variable is always done with the operator :=. This applies to local variables but also to the slots of an object. The value returned by the assignment is always the value that was assigned :
 x.age := 10John.father := mary
When the assignment depends on the former value of the variable, an implicit syntax ":op" can be used to combine the previous value with a new one using the operation op. This can be done with any (built-in or user-defined) operation (an operation is a function with arity 2 that has been explicitly declared as an operation) :
 x.age :+ 1John.friends :add maryx.price :min 100
Note that the use of :op is pure syntactical sugar: x.A :op y is equivalent to x.A := (x.A op y). The receiving expression should not, therefore, contain side-effects as in the dangerous following example :
 A(x :+ 1:+ 1


Objects, Classes and Slots
Updates
categories
Lists, Sets and Instructions
Lists, Sets and Tuples
Blocks

Lists, Sets and Instructions

Lists, Sets and Tuples

CLAIRE provides two easy means of manipulating collections of objects: sets and lists. Lists are ordered, possibly heterogeneous, collections. To create a list, one must use the list(...) instruction : it admits any number of arguments and returns the list of its arguments. Each argument to the list(...) constructor is evaluated.

 list(abcdlist(12 + 3), list()
Sets are collections without order and without duplicates. Sets are created similarly with the set(...) constructor :
 set(a,b,cset(1,2 + 3)
The major novelty in CLAIRE 3.2 is the fact that lists or sets may be typed. This means that each bag (set or list) may have a type slot named of, which contains a type to which all members of the list must belong. This type is optional, as is illustrated by the previous examples, where no typing was given for the lists or sets. To designate a type for a new list or a new set, we use a slightly different syntax :
 list<thing>(a,b,c,dlist<integer>(1,2 + 3list<float>()
 set<thing>(a,b,cset<integer>(12 + 3)
Typing a list or a set is a way to ensure that adding new values to them will not violate typing assumptions, which could happen in earlier versions of CLAIRE. Insertion is now always a destructive operation (add(l,x) returns the list l, that has been augmented with the value x at its end).

Since typing is mandatory in order to assume type-safe updates onto a list or a set, if no type is provided, CLAIRE will forbid any future update: the list or the set is then a "read-only" structure. This is the major novelty in CLAIRE 3.2: there is a difference between:
 list(a,b,c,dset(1,2 + 3list{i | i in (1 .. 2)}
which are read-only structures, and :
 list<thing>(abset<integer>(12 + 3)
 list<integer>{i | i in (1 .. 2)}
which are structures that can be updated.

List or set types can be arbitrarily complex, to represent complex list types such as list of lists of integers. However, it is recommended to use a global constant to represent a complex type that is used as a list type, as follows :
 MyList :: list<integer>
 set<MyList>(list<integer>(1), list<integer>(23))
Constant sets are valid CLAIRE types and can be built using the following syntax :
 {a,b,c,d} {38}
The expressions inside a constant set expression are not evaluated and should be primitive entities, such as integers or strings, named objects or global constants. Constant sets are constant, which means that inserting a new value is forbidden and will provoke an error.

A set can also be formed by selection. The result can either be a set with {x in a | P(x)}, or a list with list{x in a | P(x)}, when one wants to preserve the order of a and keep the duplicates if a was a list. Similarly, one may decide to create a typed or an un-typed list or set, by adding the additional type information between angular brackets. For instance, here are two samples with and without typing :
 {x in class | (thing % x.ancestors)}
 list{x in (0 .. 14| x mod 2 = 0}
 set<class>{x in class | (thing % x.ancestors)}
 list<integer>{x in (0 .. 14| x mod 2 = 0}
When does one need to add typing information to a list or a set ? A type is needed when new insertions need to be made, for instance when the list or set is meant to be stored in an object's slot which is itself typed.

Also, the imageof a set via a function can be formed. Here again, the result can either be a set with {f(x)|x in a} or a list with list{f(x) | x in a}, when one wants to preserve the order of a and the duplicates :
 {(x ^ 2| x in (0 .. 10)}
 list<integer>{size(x.slots| x in class}
For example, we have the traditional average_salary method :
 average_salary(s:set[man]) : float
     -> (sum(list{m.sal | m in s}) / size(s))
Last, two usual constructions are offered in CLAIRE to check a boolean expression universally (forall) or existentially (exists). A member of a set that satisfies a condition can be extracted (a non-deterministic choice) using the some construct: some(x in a | f(x)). For instance, we can write :
 exists(x in (1 .. 10| x > 2// returns true
 some(x in (1 .. 10| x > 2// returns 3 in most implementations
 exists(x in class | length(x.ancestors) > 10)
The difference between exists and some is that the first always returns a boolean, whereas the second returns one of the objects that satisfy the condition (if there exists one) and unknown otherwise. It is very often used in conjunction with when, as in the following example :
 when x := some(x in man | rich?(x))
 in (borrow_from(x,1000), ...)
 else printf("There is no one from whom to borrow!")
Conversely, the boolean expression forall(x in a | f(x)) returns true if and only if f(x) is true for all members of the set a. The two following examples returns false (because of 1):
 forall(x in (1 .. 10| x > 2)
 forall(x in (1 .. n| exists(y in (1 .. x| y * y > x))

Definition : A list is an ordered collection of objects that is organized into an extensible array, with an indexed access to its members. A list may contain duplicates, which are multiple occurrence of the same object. A set is a collection of objects without duplicates and without any user-defined order. The existence of a system-dependent order is language-dependent and should not be abused. The concept of bag in CLAIRE is the unifier between lists and sets : a collection of objects with possible duplicates and without order.

A read-only (untyped) list can also be thought as tuples of values. For upward compatibility reasons, the expression tuple(a1,...,an) is equivalent to list(a1,...,an) :
 tuple(1,2,3), tuple(1,2.0,"this is heterogeneous")
Since it is a read-only list, a tuple cannot be changed once it is created, neither through addition of a new member (using the method add) or through the exchange of a given member (using the nth= method). CLAIRE offers an associated data type. For instance, the following expressions are true :
 tuple(1,2,3% tuple(integer,integer,integer)
 tuple(1,2,3% tuple(0 .. 10 .. 100 .. 100)
 tuple(1,2.0,"this is heterogeneous"% tuple(any,any,any)
Typed tuples are used to return multiple values from a method. Because a tuple is a bag, it supports membership, iteration and indexed access operations. However, there is yet another data structure in CLAIRE for homogeneous arrays of fixed length, called arrays. Arrays are similar to lists but their size is fixed once they are created and they must be assigned a subtype (a type for the members of the array) that cannot change. Because of these strong constraints, CLAIRE can provide an implementation that is more efficient (memory usage and access time) than the implementation of bags. However, the use of arrays is considered an advanced feature of CLAIRE since everything that is done with an array may also be done with a list.


Lists, Sets and Tuples categories
Lists, Sets and Instructions
Blocks
Conditionals

Blocks

Parentheses can be used to group a sequence of instructions into one. In this case, the returned value is the value of the last instruction :
 (x := 3x := 5)
Parentheses can also be used to explicitly build an expression. In the case of boolean evaluation (for example in an if), any expression is considered as true except false, empty sets and empty lists :
 (1 + 2* 3
 if (x = 2 & l)
Local variables can be introduced in a block with the let construct. These variables can be typed, but it is not mandatory (CLAIRE will use type inference to provide with a reasonable type). On the other hand, unlike languages such as C++, you always must provide an initialization value when you define a variable. A let instruction contains a sequence of variable definitions and, following the in keyword, a body (another instruction). The scope of the local variable is exactly that body and the value of the let instruction is the value returned by this body.
 let x := 1y := 3 in (z := x + yy := 0)
Notice that CLAIRE uses := to represent assignment and = to represent equality. The compiler will issue a warning if a statement (x = y) is used where an assignment was probably meant (this is the case when the value of the assignment is not needed, such as in x := 1, y = 3, z := 4).

The value of local variables can be changed with the same syntax as an update to an object: the syntax :op is allowed for all operations op :
 x := x + 1x :+ 1x :2x :^ 2
The name of a local variable can be any identifier, including the name of an existing object or variable. In that case, the new variable overrides the older definition within the scope of the let. While this may prove useful in a few cases, it should be used sparingly since it yields to code that is hard to read. A rule of thumb is to avoid mixing the name of variables and the name of properties since it often produces errors that are hard to catch (the property cannot be accessed any more once a variable with the same name is defined). The control structure when is a special form of let, which only evaluates the body if the value of the local variable (unique) is not unknown (otherwise, the returned value is unknown). This is convenient to use slots that are not necessarily defined as in the following example :
 when f := get(father,x)
 in printf("his father is ~S\n"f)
The default behavior when the value is unknown can be specified using the else keyword. The statement following the else keyword will be evaluated and its value will be returned when the value of the local variable is unknown :
 when f := get(father,x)
 in printf("his father is ~S\n"f)
 else printf("his father is not known at the present time\n")
Local variables can also be introduced as a pattern, that is a tuple of variables. In that case, the initial value must be a tuple of the right length. For instance, one could write :
 let (xyz:= tuple(123in x + y + z
The tuple of variable is simply introduced as a sequence of variables surrounded by two parentheses. The most common use of this form is to assign the multiple values returned by a function with range tuple, as we shall see in the next section. If we suppose that f is a method that returns a tuple with arity 2, then the two following forms are equivalent:
 let (x1,x2:= f() in ...

 let l := f(), x1 := l[1], x2 := l[2in ...
[XL] In XL CLAIRE, as a syntactical shortcut, we can define in a single let statement both tuple assigment and normal variable assigment as in :
 let (x1,x2:= f(),
     x3 := g() in ...
Tuples of variables can also be assigned directly within a block as in the following example :
 (x1x2:= tuple(x2x1)
Although this mostly used for assigning the result of tuple-valued functions without any useless allocation, it is interesting to note that the previous example will be compiled into a nice value-exchange interaction without any allocation (the compiler is smart enough to determine that the list "list(x2,x1)" is not used as such).

The key principle of lexical variables is that they are local to the "let" in which they are defined. CLAIRE supports another similar type of block, which is called a temporary slot assignment. The idea is to change the value of a slot but only locally, within a given expression. This is done as follows:
 let x.r := y in e
changes the value of r(x) to y, executes e and then restore r(x) to its previous value. It is strictly equivalent to
 let old_v := x.r
 in (x.r := y,
     let result := e
     in (x.r := old_vresult))
CLAIRE provides automatic type inference for variables that are defined in a let so that explicit typing is not necessary in most of the cases. Here are a few rules to help you decide if you need to add an explicit type to your variable or even cast a special type for the value that is assigned to the variable :

  1. (a) Type inference will provide a type to a Let variable only if they do not have one already.
  2. (b) when you provide a type in let x:t := y, the compiler will check that the value y belong to t and will issue a warning and/or insert a run-time type-check accordingly.
  3. (c) if you want to force the type that is inferred to something smaller than what CLAIRE thinks for y, you must use a cast :
     let x := (y as t2in ...
To summarize :


Blocks categories
Lists, Sets and Instructions
Conditionals
Loops

Conditionals

if statements have the usual syntax (if <test> x else y) with implicit nestings (else if). The <test> expression is evaluated and the instruction x is evaluated if the value is different from false, nil or {}. Otherwise, the instruction y is evaluated, or the default value false is returned if no else part was provided.
 if (x = 1x := f(x,y)
 else if (x > 1x := g(x,y)
 else (x := 3f(x,y))

 if (let y := 3 in x + y > 4 / xprint(x)
If statements must be inside a block, which means that if they are not inside a sequence surrounded by parenthesis they must be themselves surrounded by parenthesis (thus forming a block).

case is a set-based switch instruction: CLAIRE tests the branching sets one after another, executes the instruction associated with the first set that contains the object and exits the case instruction without any further testing. Hence, the default branch is associated with the set any. As for an if, the returned value is nil if no branch of the case is relevant :
 case x ({1x + 1, {2,3x + 2any x + 3)
 case x (integer (x := 3print(x)),
         any error("~I is no good\n",x))
Note that the compiler will not accept a modification of the variable that is not consistent with the branch of the case (such as case x ({1} x := 2)). The expression on which the switching is performed is usually a variable, but can be any expression. However, it should not produce any side effect since it will be evaluated many times.

Starting with CLAIRE 3.3, only boolean expressions should be used in the <test> expression of a conditional statement. The implicit coercion of any expression into a Boolean is still supported, but should not be used any longer. The compiler will issue a warning if a non-boolean expression is used in an If.


Conditionals categories
Lists, Sets and Instructions
Loops
Instantiation

Loops

CLAIRE supports two types of loops: iteration and conditional loops (while and until). Iteration is uniquely performed with the for statement, it can be performed on any collection :
 for x in (1 .. 3a[x:= a[x + 3]
 for x in list{x in class | size(x.ancestors) >= 4}
     printf("~S \n"x)
A collection here is taken in a very general sense, i.e., an object that can be seen as a set through the enumeration method set!. This includes all CLAIRE types but is not restricted since this method can be defined on new user classes that inherit from the collection root. For instance, set!(n:integer) returns the subset of (0 .. 29) that is represented by the integer n taken as a bit-vector. To tell CLAIRE that her new class is a collection, the user must define it as a subclass of collection. If x is a collection, then :
 for z in x ...

 (z % x)
are supported. When defining a new subclass of collection, the methods set! and % must be defined for this new class, and it is also advisable to define size and iterate to get compiler speed-ups (if size is not defined, an implicit call to set! is made). Other collection handling methods, such as add, delete, etc may be defined freely if needed.

Notice that it is possible that the expression being evaluated inside the loop modifies the set itself, such as in :
 for x in {y in S | P(y)} P(x:= false
Because the CLAIRE compiler tries to optimize iteration using lazy evaluation, there is no guarantee about the result of the previous statement. In this case, it is necessary to use an explicit copy as follows :
 for x in copy({y in S | P(y)}) P(x:= false
The iteration control structure plays a major role in CLAIRE. It is possible to optimize its behavior by telling CLAIRE how to iterate a new subclass (C) of collection. This is done through adding a new restriction of the property iterate for this class C, which tells how to apply a given expression to all members of an instance of C. This may avoid the explicit construction of the equivalent set which is performed through the set! method.

Conditional loops are also standard (the exiting condition is executed before each loop in a while and after each loop in a until),
 while (x > 0x :+ 1
 until (x = 12x :+ 1
 while not(i = size(l))
     (l[i:= 1i :+ 1)
The value of a loop is false. However, loops can be exited with the break(x) instruction, in which case the return value is the value of x :
 for x in class (if (x % subtype[integer]) break(x))
There is one restriction with the use of break: it cannot be used to escape from a try ... catch block. This situation will provoke an error at compile-time.


Loops categories
Lists, Sets and Instructions
Instantiation
Exception Handling

Instantiation

Instantiation is the mechanism of creating a new object of a given class; instantiation is done by using the class as a selector and by giving a list of "<slot> = <value>" pairs as arguments :
 complex(re = 0.0im = 1.0)
 person(age = 0father = john)
Recall that the list of instances of a given class is only kept for non-ephemeral classes (a class is ephemeral if has been declared as such or if it inherits from the ephemeral_object class). The creation of a new instance of a class yields to a function call to the method close. Objects with a name are represented by the class thing, hence descendents of thing (classes that inherit from thing) can be given a name with the definition operation ::. These named objects can later be accessed with their name, while objects with no name offer no handle to manipulate them after their creation outside of their block (objects with no name are usually attached to a local variable with a let whenever any other operation other than the creation itself is needed) :
 paul :: person(age = 10father = peter)
Notice that the identifier used as the name of an object is a constant that cannot be changed. Thus, it is different from creating a global variable that would contain an object as in :
 aGoodGuy:person :: person(age = 10father = peter)
Additionally, there is a simpler way of instantiating parameterized classes by dropping the slot names. All values of the parameter slots must be provided in the exact order that was used to declare the list of parameters. For instance, we could use :
 complex(0.0,1.0), stack(integer)
The previously mentioned instantiation form only applies to a parameterized class. It is possible to instantiate a class that is given as a parameter (say, the variable v) using the new method. New(v) creates an instance of the class v and new(v,s) creates a named instance of the class v (assumed to be a subclass of thing) with the name s.


Instantiation categories
Lists, Sets and Instructions
Exception Handling
array

Exception Handling

Exceptions are a useful feature of software development: they are used to describe an exceptional or wrong behavior of a block. Exception can be raised, to signal this behavior and are caught by exception handlers that surround the code where the exceptional behavior happened. Exceptions are CLAIRE objects (a descendent from the class exception) and can contain information in slots. The class exception is an "ephemeral" class, so the list of instances is not kept. In fact, raising an exception e is achieved by creating an instance of the class e. Then, the method close is called: the normal flow of execution is aborted and the control is passed to the previously set dynamic handler. A handler is created with the following instruction :
 try <expressioncatch <class> <expression>
For instance we could write :
 try 1 / x
 catch any (printf("1/~A does not exists"x), 0)
A handler "try e catch c f", associated with a class c, will catch all exceptions that may occur during the evaluation of e as long as they belong to c. Otherwise the exception will be passed to the previous dynamic handler (and so on). When a handler "catches" an exception, it evaluates the "f" part and its value is returned. The last exception that was raised can be accessed directly with the exception!() method. Also, as noticed previously, the body of a handler cannot contain a break statement that refers to a loop defined outside the handler.

The most common exceptions are errors and there is a standard way to create an error in CLAIRE using the error(s:string, l:listargs) instruction. This instruction creates an error object that will be printed using the string s and the arguments in l, as in a printf statement. Here are a few examples :
 error("stop here")
 error("the value of price(~S) is ~S !"xprice(x))
Another very useful type of exception is contradiction. CLAIRE provides a class contradiction and a method contradiction!() for creating new contradictions. This is very commonly used for hypothetical reasoning with forms like :
 try (choice(), // create a new world
     ...)       // performs an update that may cause a contradiction
 catch contradiction
     (backtrack(), // return to previous world
     ...)
In fact, this is such a common pattern that CLAIRE provides a special instruction, branch(x), which evaluates an expression inside a temporary world and returns a boolean value, while detecting possible contradiction. The statement branch(x) is equivalent to :
 try (choice(),
     if x true else (backtrack(), false))
 catch contradiction (backtrack(), false)
If we want to find a value for the slot x.r among a set x.possible that does not cause a contradiction (through rule propagation) we can simply write :
 when y := some(y in x.possible | branch(x.r = y))
 in x.r := y
 else contradiction!()


Exception Handling categories
Lists, Sets and Instructions
array
Methods and Types
Methods

array

An array can be seen as a fixed-size list, with a member type (the slot name is of), which tells the type of all the members of the array. Because of the fixed size, the compiler is able to generate faster code than when using lists, so lists should be used when the collection shrinks and grows, and an array may be used otherwise. This is especially true for arrays of floats, which are handled in a special (and efficient) way by the compiler.

Arrays are simpler than lists, and only a few operations are supported. Therefore, more complex operations such as append often require a cast to list (list!). An array is created explicitly with the make_array property :
 let l := make_array(10,float,0.0)
 in l[1:= l[3+ l[4]
Operations on arrays include copying, casting a bag into an array (array!), defeasible update on arrays using store, and returning the length of the array with length. An array can also be made from a list using array!, which is necessary to create arrays that contain complex objects (such as arrays of arrays).


Lists, Sets and Instructions
array
categories
Methods and Types
Methods
Types

Methods and Types

Methods

A method is the definition of a property for a given signature. A method is defined by the following pattern : a selector (the name of the property represented by the method), a list of typed parameters (the list of their types forms the domain of the method), a range expression and a body (an expression or a let statement introduced by -> or =>).
 <selector>(<typed parameters>) : <range>opt < -> | => > <body>

 fact(n:{0}) : integer -> 1
 fact(n:integer: integer -> (n * fact(n - 1))
 print_test() : void
     -> (print("Hello"),
         print("world\n")

Definition : A signature is a Cartesian product of types that always contains the extension of the function. More precisely, a signature A1* A2* ... * An, also represented as list(A1, ...,An) or A1* A2* ... * An-1 -> An, is associated to a method definition f(...) : An -> ... for two purposes: it says that the definition of the property f is only valid for input arguments (x1, x2, ..., xn-1) in A1* A2* ... * An-1 and it says that the result of f(x1, x2, ..., xn-1) must belong to An. The property f is also called an overloaded function and a method m is called its restriction to A1* A2* ... * An-1.

If two methods have intersecting signatures and the property is called on objects in both signatures, the definition of the method with the smaller domain is taken into account. If the two domains have a non-empty intersection but are not comparable, a warning is issued and the result is implementation-dependent. The set of methods that apply for a given class or return results in another can be found conveniently with methods.

The range declaration can only be omitted if the range is void. In particular, this is convenient when using the interpreter :
 loadMM()
 -> (begin(my_module),
     load("f1"),
     load("f2"),
     end(my_module))
If the range is void (unspecified), the result cannot be used inside another expression (a type-checking error will be detected at compilation). A method's range must be declared void if it does not return a value (for instance, if its last statement is, recursively, a call to another method with range void). It is important not to mix restrictions with void range with other regular methods that do return a value, since the compiler will generate an error when compiling a call unless it can guarantee that the void methods will not be used.

The default range was changed to void in the version 3.3 of CLAIRE, in an effort to encourage proper typing of methods: "no range" means that the method does not return a value. This is an important change when migrating code from earlier versions of CLAIRE. CLAIRE supports methods with a variable number of arguments using the listargs keyword. The arguments are put in a list, which is passed to the (unique) argument of type listargs. For instance, if we define :
 [f(x:integer,y:listargs-> x + size(y)]
A call f(1,2,3,4) will produce the binding x = 1 and y = list(2,3,4) and will return 4.

CLAIRE also supports functions that return multiple values using tuples. If you need a function that returns n values v1,v2,...,vn of respective types t1,t2,...,tn, you simply declare its range as tuple(t1,t2,...,tn) and return tuple(v1,v2,...,vn) in the body of the function. For instance the following method returns the maximum value of a list and the "regret" which is the difference between the best and the second-best value :
 [my_max(l:list[integer]) : tuple(integer,integer)
 -> let x1 := 1000000000x2 := 1000000000
     in (for y in l
         (if (y < x1) (x2 := x1x1 := y)
         else if (y < x2x2 := y),
         tuple(x1,x2))]
The tuple produced by a tuple-valued method can be used in any way, but the preferred way is to use a tuple-assignment in a let. For instance, here is how we would use the max2 method :
 let (a,b:= my_max(list{f(i| i in (1 .. 10)})
 in ...
Each time you use a tuple-assignment for a tuple-method, the compiler uses an optimization technique to use the tuple virtually without any allocation. This makes using tuple-valued methods a safe and elegant programming technique.

The body of a method is either a CLAIRE expression (the most common case) or an external (C++) function. In the first case, the method can be seen as defined by a lambda abstraction. This lambda can be created directly through the following :
 lambda[(<typed parameters>), <body>]
Defining a method with an external function is the standard way to import a C/C++ function in CLAIRE. This is done with the function!(...) constructor, as in the following :
 f(x:integer,y:integer-> function!(my_version_of_f)
 cos(x:float-> function!(cos_for_claire)
It is important to notice that in CLAIRE, methods can have at most 20 parameters. Methods with 40 or more parameters that exist in some C++ libraries are very hard to maintain. It is advised to use parameter objects in this situation.

CLAIRE also provides inline methods, that are defined using the => keyword before the body instead of ->. An inline method behaves exactly like a regular method. The only difference is that the compiler will use in-line substitution in its generated code instead of a function call when it seems more appropriate. Inline methods can be seen as polymorphic macros, and are quite powerful because of the combination of parametric function calls (using call(...)) and parametric iteration (using for). Let us consider the two following examples, where subtype[integer] is the type of everything that represents a set of integers :
 sum(s:subtype[integer]) : integer
     => let x := 0 in (for y in s x :+ yx)

 min(s:subtype[integer], f:property: integer
     => let x := 0empty := true
         in (for y in s
             (if empty (x := yempty := false)
             else if call(f,y,xx := y),
             x)
For each call to these methods, the compiler performs the substitution and optimizes the result. For instance, the optimized code generated for sum({x.age | x in person}) and for min({x in 1 .. 10 | f(x) > 0}, >) will be :
 let x := 0
 in (for %v in person.instances
         let y := %v.age in x :+ yx)

 let x := 0empty := truey := 1max := 10
 in (while (y <= max)
         (if (f(y) > 0)
             (if empty (x := yempty := false)
             else if (y > xx := y),
         y :+ 1),
     x)
Notice that, in these two cases, the construction of temporary sets is totally avoided. The combined use of inline methods and functional parameters provides an easy way to produce generic algorithms that can be instantiated as follows :
 mymin(l:list[integer]) : integer -> min(lmy_order)
The code generated for the definition of mymin @ list[integer] will use a direct call to my_order (with static binding) and the efficient iteration pattern for lists, because min is an inline method. In that case, the previous definition of min may be seen as a pattern of algorithms.

For upward compatibility reasons (from release 1.0), CLAIRE still supports the use of external brackets around method definitions. The brackets are there to represent boxes around methods (and are pretty-printed as such with advanced printing tools). For instance, one can write :
 [mymin(l:list[integer]) : integer -> min(lmy_order)]
Brackets have been found useful by some users because one can search for the definition of the method m by looking for occurrences of '[mmm'. They also transform a method definition into a closed syntactical unit that may be easier to manipulate (e.g., cut-and-paste).

When a new property is created, it is most often implicitly with the definition of a new method or a new slot, although a direct instantiation is possible. Each property has an extensibility status that may be one of :

The compiler will automatically change the status from undefined to closed, unless the status is forced with the abstract declaration :
 abstract(p)
Conversely, the final declaration :
 final(p)
may be used to force the status to closed, in the interpreted mode. Note that these two declarations have obviously an impact on performance: an open property will be compiled with the systematic used of dynamic calls, which ensures the extensibility of the compiled code, but at a price. On the contrary, a final property will enable the compiler to use as much static binding as possible, yielding faster call executions. Notice that the interface(p) declaration has been introduced to support dynamic dispatch in a efficient manner, as long as the property is uniform.


Methods categories
Methods and Types
Types
Polymorphism

Types

CLAIRE uses an extended type system that is built on top of the set of classes. Like a class, a type denotes a set of objects, but it can be much more precise than a class. Since methods are attached to types (by their signature), this allows attaching methods to complex sets of objects.

Definition : A (data) type is an expression that represents a set of objects. Types offer a finer-granularity partition of the object world than classes. They are used to describe objects (range of slots), variables and methods (through their signatures). An object that belongs to a type will always belong to the set represented by the type.

Any class (even parameterized) is a type. A parameterized class type is obtained by filtering a subset of the class parameters with other types to which the parameters must belong. For instance, we saw previously that complex[im:{0.0}] is a parametrized type that represent the real number subset of the complex number class. This also applies to typed lists or sets which use the of parameter. For instance, list[of:{integer}] is the set of list whose of parameter is precisely integer. Since these are common patterns, CLAIRE offers two shortcuts for parameterized type expressions. First, it accepts the expression C[p = v] as a shortcut for C[p:{v}]. Second, it accepts the expression C<X> as a shortcut for C[of = X]. This applies to any class with a type-valued parameter named of; Thus, stack<integer> is the set of stacks whose parameter "of" is exactly integer, whereas stack[of:subtype[integer]] is the set of stacks whose parameter (a type) is a subset of integer.

Finite constant sets of objects can also be used as types. For example, {john, jack, mary} and {1,4,9} are types. Intervals can be used as types; the only kind of intervals supported by CLAIRE 3.0 is integer intervals. Types may also formed using the two intersection (^) and union (U) operations. For example, integer U float denotes the set of numbers and (1 .. 100) ^ (-2 .. 5) denotes the intersection of both integer intervals, i.e. (1 .. 5).

Subtypes are also as type expressions. First, because types are also objects, CLAIRE introduces subtype[t] to represent the set of all type expressions that are included in t. This type can be intersected with any other type, but there are two cases which are more useful than other, namely subtypes of the list and set classes. Thus, CLAIRE uses set[t] as a shortcut for set ^ subtype[t] and list[t] as a shortcut for list ^ subtype[t]. Because of the semantics of lists, one may see that list[t] is the union of two kinds of lists :

Therefore, there is a clear difference between Obviously, we have list<t> <= list[t]. When should you use one or the other form of typed lists or sets ?
  1. use list[t] to type lists that will only be used by accessing their content. A method that uses l:list[t] in its signature will be polymorphic, but updates on l will rely on dynamic (run-time) typing.
  2. use list<t> to type lists that need to be updated. A method that uses l:list<t> in its signature will be monomorphic (i.e., will not work for l:list<t'> with t' <= t), but updates will be statically type-checked (at compile time).
Last, CLAIRE uses tuple and array types. The array type t[] represents arrays whose member type is t (i.e., all members of the array belong to t). Tuples are used to represent type of tuples in a very simple manner: tuple(t1,t2,...,tn) represents the set of tuples tuple(v1,v2, ... ,vn) such that vi belong to ti for all i in (1 .. n). For instance, tuple(integer, char) denotes the set of pair tuples with an integer as first element and a character as second. Also you will notice that tuple(class,any,type) belongs to itself, since class is a class and type is a type.

Classes are sorted with the inheritance order. This order can be extended to types with the same intuitive meaning that a type t1 is a subtype of a type t2 if the set represented by t1 is a subset of that represented by t2. The relation "t1 is a subtype of a type t2" is noted t1 <= t2. This order supports the introduction of the " subtype " constructor: subtype[t] is the type of all types that are less than t.


Types categories
Methods and Types
Polymorphism
Escaping Types

Polymorphism

In addition to the traditional "objet-oriented" polymorphism, CLAIRE also offers two forms of parametric polymorphism, which can be skipped by a novice reader.

(1) There often exists a relation between the types of the arguments of a method. Capturing such a relation is made possible in CLAIRE through the notion of an "extended signature". For instance, if we want to define the operation "push" on a stack, we would like to check that the argument y that is being pushed on the stack s belongs to the type of(s), that we know to be a parameter of s. The value of this parameter can be introduced as a variable and reused for the typing of the remaining variables (or the range) as follows :
 push(s:stack<X>, y:X-> (s.content :add ys.index :+ 1)
The declaration s:stack<X> introduced X as a type variable with value s.of, since stack[of] was defined as a parameterized class. Using X in y:X simply means that y must belong to the type s.of. Such intermediate type variables must be free identifiers (the symbol is not used as the name of an object) and must be introduced with the following template :
 <class>[pi=vi...]
The use of type variables in the signature can be compared to pattern matching. The first step is to bind the type variable. If (p = V) is used in c[ ...] instead of p:t, it means that we do not put any restriction on the parameter p but that we want to bind its value to V for further use. Note that this is only interesting if the value of the parameter is a type itself. Once a type variable V is defined, it can be used to form a pattern (called a <type with var> in the CLAIRE syntax) as follows:

 <type with var= <type| <var| {<var>} |
     tuple(<type with var>seq+|
     <class>[< <var>:<type with var| <var>=<var> >seq+]
(2) The second advanced typing feature of CLAIRE is designed to capture the fine relationship between the type of the output result and the types of the input arguments. When such a relationship can be described with a CLAIRE expression e(x1,...,xn), where x1, ..., xn are the types of the input parameters, CLAIRE allows to substitute type[e] to the range declaration. It means that the result of the evaluation of the method should belong to e(t1,...,tn) for any types t1,...,tn that contain the input parameters.

For instance, the identity function is known to return a result of the same type as its input argument (by definition !). Therefore, it can be described in CLAIRE as follows :
 id(x:any: type[x-> x
In the expression that we introduce with the type[e] construct, we can use the types of the input variables directly through the variables themselves. For instance, in the "type[x]" definition of the id example, the "x" refers to the type of the input variable. Notice that the types of the input variables are not uniquely defined. Therefore, the user must ensure that her "prediction" e of the output type is valid for any valid types t1, ..., tn of the input arguments.

The expression e may use the extra type variables that were introduced earlier. For instance, we could define the "top" method for stacks as follows :
 top(s:stack<X>) : type[X-> s.content[s.index]
The "second-order type" e (second-order means that we type the method, which is a function on objects, with another function on types) is built using the basic CLAIRE operators on types such as U, ^ and some useful operations such as "member". If c is a type, member(c) is the minimal type that contains all possible members of c. For instance, member({c}) = c by definition. This is useful to describe the range of the enumeration method set!. This method returns a set, whose members belong to the input class c by definition. Thus, we know that they must belong to the type member(X) for any type X to whom c belongs (cf. definition of member). This translates into the following CLAIRE definition :
 set!(c:class: type[set[member(c)]] -> c.instances
For instance, if c belongs to subtype[B] then set!(c) belongs to set[B].

To summarize, here is a more precise description of the syntax for defining a method :
 <function>(<vi>:<ti>i E (1 .. n): <range-> <exp>
Each type ti for the variable vi is an "extended type" that may use type variables introduced by the previous extended types t1, t2 ... ti-1 . An extended type is defined as follows :
 <et= <class| <set| <var| (<et^ | U <et>) | (<integer.. <integer>) |
     set[<et>] | list[<et>] | <et>[] | tuple(<et>seq|
     <class>[< <var>:<et| <var>=< <var| <const> > >seq+]
The <range> expression is either a regular type or a "second order type", which is a CLAIRE expression e introduced with the type[e] syntactical construct :
 <range= <type| type[<expression>]


Polymorphism categories
Methods and Types
Escaping Types
Selectors, Properties and Operations

Escaping Types

There are two ways to escape type checking in CLAIRE. The first one is casting, which means giving an explicit type to an expression. The syntax is quite explicit :
 <cast= (<expressionas <type>)
This will tell the compiler that <expression> should be considered as having type <type>. Casting is ignored by the interpreter and should only be used as a compiler optimization. There is, however, one convenient exception to this rule, which is the casting into a list parametric type. When an untyped list is casted into a typed list, the value of its of parameter is actually modified by the interpreter, once the correct typing of all members has been verified. For instance, the two following expressions are equivalent :
 list<integer>(1,2,3,4)
 list(1,2,3,4as list<integer>
The second type escaping mechanism is the non-polymorphic method call, where we tell what method we want to use by forcing the type of the first argument. This is equivalent to the supermessage passing facilities of many object-oriented languages.
 <super= <selector>@<type>(<exp>seq)
The instruction f@c(...) will force CLAIRE to use the method that it would use for f(...) if the first argument was of type c (CLAIRE only checks that this first argument actually belongs to c).

A language is type-safe if the compiler can use type inference to check all type constraints (ranges) at compile-time and ensure that there will be no type checking errors at run-time. CLAIRE is not type-safe because it admits expressions for which type inference is not possible such as read(p) + read(p). On the other hand, most expressions in CLAIRE may be statically type-checked and the CLAIRE compiler uses this property to generate code that is very similar to what would be produced with a C++ compiler. A major difference between CLAIRE 3.0 and earlier versions is the fact that lists may be explicitly typed, which removes the problems that could happen earlier with dynamic types. Lists and sets subtypes support inclusion polymorphism, which means that if A is a subtype of B, list[A] is a subtype of list[B]; for instance list[(0 .. 1)] <= list[integer]. Thus only read operations can be statically type-checked w.r.t. such type information. On the other hand, array subtypes, as well as list or set parametric subtypes, are monomorphic, since A[] is not the set of arrays which contain members of A, but the set of arrays whose member type (the of slot) contains the value A. Thus if A is different from B, A[] is not comparable with B[], and list<A> is not comparable with list<B>. This enables the static type-checking of read and write operations on lists. The fact that CLAIRE supports all styles of type disciplines is granted by the combination of a rich dynamic type system coupled with a powerful type inference mechanism within the compiler, and is a key feature of CLAIRE.


Escaping Types categories
Methods and Types
Selectors, Properties and Operations
Iterations

Selectors, Properties and Operations

As we said previously, CLAIRE supports two syntaxes for using selectors, f(...) and (.... f ....). The choice only exists when the associated methods have exactly two arguments. The ability to be used with an infix syntax is attached to the property f :
 f :: operation()
Once f has been declared as an operation, CLAIRE will check that it is used as such subsequently. Restrictions of f can then be defined with the usual syntax :
 f(x:integery:integer: ...
Note that declaring f as an operation can only be done when no restriction of f is known. If the first appearance of f is in the declaration of a method, f is considered as a normal selector and its status cannot be changed thereafter. Each operation is an object (inherits from property) with a precedence slot that is used by the reader to produce the proper syntax tree from expressions without parentheses.
 gcd :: operation(precedence = precedence(/))
 12 + 3 gcd 4 // same as 12 + (3 gcd 4)
So far we have assumed that any method definition is allowed, provided that inheritance conflict may cause warning. Once a property is compiled, CLAIRE uses a more restrictive approach since only new methods that have an empty intersection with existing methods (for a given property) are allowed. This allows the compiler to generate efficient code. It is possible to keep the "open" status of a property when it is compiled through the abstract declaration.
 abstract(f)
Such a statement will force CLAIRE to consider f as an "abstract" parameter of the program that can be changed at any time. In that case, any re-definition of f (any new method) will be allowed. When defining a property parameter, one should keep in mind that another user may redefine the behavior of the property freely in the future.

It is sometimes useful to model a system with redundant information. This can be done by considering pairs of relations inverse one of another. In this case the system maintains the soundness of the database by propagating updates on one of the relations onto the other. For example if husband is a relation from the class man onto the class woman and wife a relation from woman to man, if moreover husband and wife have been declared inverse one of another, each modification (addition or retrieval of information) on the relation husband will be propagated onto wife. For example husband(mary) := john will automatically generate the update wife(john) := mary. Syntactically, relations are declared inverses one of another with the declaration :
 inverse(husband:= wife
This can be done for any relation: slots and tables. Inverses introduce an important distinction between multi-valued relations and mono-valued relations. A relation is multi-valued in CLAIRE when its range is a subset of bag (i.e. a set or a list). In that case the slot multivalued? of the relation is set to true and the set associated with an object x is supposed to be the set of values associated with x through the relation.

This has the following impact on inversion. If r and s are two mono-valued relations inverse one of another, we have the following equivalence :
 s(x= y <=> r(y= x
In addition, the range of r needs to be included in the domain of s and conversely. The meaning of inversion is different if r is multi-valued since the inverse declaration now means :
 s(x= y <=> x E r(y)
Two multi-valued relations can indeed be declared inverses one of another. For example, if parents and children are two relations from person to set[person] and if inverse(children) = parents, then :
 children(x= {y in person | x % parents(y)}
Modifications to the inverse relation are triggered by updates (with :=) and creations of objects (with filled slots). Since the explicit inverse of a relation is activated only upon modifications to the database (it is not retroactive), one should always set the declaration of an inverse as soon as the relation itself is declared, before the relation is applied on objects. This will ensure the soundness of the database. To escape the triggering of updates to inverse relations, the solution is to fill the relation with the method put instead of :=. For example, the following declaration :
 let john := person() in (put(wife,john,mary), john)
does the same as :
 john :: person(wife = mary)
without triggering the update husband(mary) := john.


Selectors, Properties and Operations categories
Methods and Types
Iterations
Tables, Rules and Hypothetical Reasoning
Tables

Iterations

We just saw that CLAIRE will produce in-line substitution for some methods. This is especially powerful when combined with parametric function calls (using call(...)) taking advantage of the fact that CLAIRE is a functional language. There is another form of code substitution supported by CLAIRE that is also extremely useful, namely the iteration of set data structure.

Any object s that understands the set! method can be iterated over. That means that the construction for x in s e(x) can be used. The actual iteration over the set represented by s is done by constructing explicitly the set extension. However, there often exists a way to iterate the set structure without constructing the set extension. The simplest example is the integer interval structure that is iterated with a while loop and a counter.

It is possible to define iteration in CLAIRE through code substitution. This is done by defining a new inline restriction of the property iterate, with signature (x:X,v:Variable,e:any). The principle is that CLAIRE will replace any occurrence of (for v in x e) by the body of the inline method as soon as the type of the expression x matches with X (v is assumed to be a free variable in the expression e). For instance, here is the definition of iterate over integer intervals :
 iterate(x:interval[min:integer,max:integer],v:Variable,e:any=>
     let v := min(x), %max := max(x)
     in (while (v <= %max) (ev :+ 1))
Here is a more interesting example. We can define hash tables as follows. A table is defined with a list (of size 2n - 3, which is the largest size for which a chunk of size 2n is allocated), which is full of "unknown" except for these objects that belong to the set. Each object is inserted at the next available place in the table, starting at a point given by the hashing function (a generic hashing function provided by CLAIRE: hash) :
 htable <: object(count:integer = 0,
                 index:integer = 4,
                 arg:list<any= list<any>())

 set!(x:htable: set -> {y in x.arg | known?(y)}

 insert(x:htabley:any->
     let l := x.arg
     in (if (x.count >= length(l) / 2)
         (x.arg := make_list(^2(x.index - 3), unknown),
         x.index :+ 1x.count := 0,
         for z in {y in l | known?(y)} insert(x,z),
             insert(x,y))
     else let i := hash(l,y)
         in (until (l[i= unknown | l[i= y)
                 (if (i = length(l)) i := 1 else i :+ 1),
                 if (l[i= unknown)
                     (x.count :+ 1l[i:= y)))
Note that CLAIRE provides a few other functions for hashing that would allow an even simpler, though less self-contained, solution. To iterate over such hash tables without computing set!(x) we define :
 iterate(s:htablev:Variablee:any)
     => (for v in s.arg (if known?(ve))
Thus, CLAIRE will replace :
 let s:htable := ... in sum(s)
by :
 let s:htable := ... in
     (let x := 0
     in (for v in s.arg
         (if known?(vx :+ v),
         x))
The use of iterate will only be taken into account in the compiled code unless one uses oload, which calls the optimizer for each new method. iterate is a convenient way to extend the set of CLAIRE data structure that represent sets with the optimal efficiency. Notice that, for a compiled program, we could have defined set! as follows (this definition would be valid for any new type of set) :
 set!(s:htable-> {x | x in s}
When defining a restriction of iterate, one must not forget the handling of values returned by a break statement. In most cases, the code produce by iterate is itself a loop (a for or a while), thus this handling is implicit. However, there may be multiples loops, or the final value may be distinct from the loop itself, in which case an explicit handling is necessary. Here is an example taken from class iteration :
 iterate(x:classv:Variablee:any: any
     => (for %v_1 in x.descendents
         let %v_2 := (for v in %v_1.instances e// catch inner break
         in (if %v_2 break(%v_2)))  // transmit the value
Notice that it is always possible to introduce a loop to handle breaks if none are present; we may replace the expression e by :
 while true (ebreak(nil))
Last, we need to address the issue of parametric polymorphism, or how to define new kinds of type sets. The previous example of hash-sets is incomplete, because it only describes generic hash-sets that may contain any element. If we want to introduce typed hash-sets, we need to follow these three steps. First we add a type parameter to the htable class :
 htable[of<: object(of:type = anycount:integer = 0...)
Second, we use a parametric signature to use the type parameter appropriately :
 insert(x:htable<X>, y:X-> ...
Last, we need to tell the compiler that an instance from htable[X] only contains objects from X. This is accomplished by extending the member function which is used by the compiler to find a valid type for all members of a given set. If x is a type, member(x) is a valid type for any y that will belong to a set s of type x. If T is a new type of sets, we may introduce a method member(x :T, t :type) that tells how to compute member(t) if t is included in T. For instance, here is a valid definition for our htable example :
 member(x:htable,t:type-> member(t @ of)
This last part may be difficult to grasp (do not worry, this is an advanced feature). First, recall that if t is a type and p a property, (t @ p) is a valid type for x.p when x is of type t. Suppose that we now have an expression e, with type t1, that represents a htable (thus t1 <= htable). When the compiler calls member(t1), the previous method is invoked (x is bound to a system-dependent value that should not be used and t is bound to t1). The first step is to compute (t1 @ of), which is a type that contains all possible values for y.of, where y is a possible result of evaluating e. Thus, member(t1 @ of) is a type that contains all possible values of y, since they must belong to y.of by construction. This type is, therefore, used by the compiler as the type of the element variable v inside the loop generated by iterate.


Methods and Types
Iterations
categories
Tables, Rules and Hypothetical Reasoning
Tables
Rules

Tables, Rules and Hypothetical Reasoning

Tables

Named arrays, called tables, can be defined in CLAIRE with the following syntax :
 <name>[var:<domain>] : <type:= <expression(var)>
The <type> is the range of the table and <expression> is an expression that is used to fill the table. This expression may either be a constant or a function of the variables of the table (i.e., an expression in which the variables appear). If the expression is a constant, it is implicitly considered as a default value, the domain of the table may thus be infinite. If the default expression is a function, then the table is filled when it is created, so the domain needs to be finite. When one wants to represent incomplete information, one should fill this spot with the value unknown. For instance, we can define :
 square[x:(0 .. 20)] : integer := (x * x)
 creator[x:class: string := "who created that class"
 maximum[x:set[0 .. 10]] : integer := (if x min(x,> @ integerelse 0)
Tables can be accessed through square brackets and can be modified with assignment expressions like for local variables :
 square[1], square[2:= 4square[4:+ 5
We can also define two-dimensional tables such as :
 distance[x:tuple(city,city)] : integer := 0
 cost[x:tuple(1 .. 101 .. 10)] : integer := 0
The proper way to use such a table is distance[list(denver,miami)] but CLAIRE also supports distance[denver,miami]. CLAIRE also supports a more straightforward declaration such as :
 cost[x:(1 .. 10), y:(1 .. 10)] : integer := 0
Last, tables can be defined in an unamed fashion through the method make_table, such unamed can be collected by the GC :
 let square := make_table((1 .. 10), integer0)
 in (for n in (1 .. 10))
         square[n:= n * n,
     ...)


Tables categories
Tables, Rules and Hypothetical Reasoning
Rules
Hypothetical Reasoning

Rules

A rule in CLAIRE is made by associating an event condition to an expression. The rule is attached to a set of free variables of given types: each time that an event that matches the condition becomes occurs for a given binding of the variables (i.e., association of one value to each variable), the expression will be evaluated with this binding. The interest of rules is to attach an expression not to a functional call (as with methods) but to an event, with a binding that is more flexible (many rules can be combined for one event) and more incremental.

Definition : A rule is an object that binds a condition to an action, called its conclusion. Each time the condition becomes true for a set of objects because of a new event, the conclusion is executed. The condition is expressed as a logic formula on one or more free variables that represent objects to which the rule applies. The conclusion is a CLAIRE expression that uses the same free variables. An event is an update on these objects, either the change of a slot or a table value, or the instantiation of a class. A rule condition is checked if and only if an event has occurred.

A novelty in CLAIRE 3.0 is the introduction of event logic. There are two events that can be matched precisely: the update of a slot or a table, and the instantiation of a class. CLAIRE 3.2 use expressions called event pattern to specify which kind of events the rule is associated with. For instance, the expression x.r := y is an event expression that says both that x.r = y and that the last event is actually the update of x.r from a previous value. More precisely, here are the events that are supported :

Note that an update of the type x.r :delete y (resp. a[x] :delete y), where r is a slot of x (resp. a is a table), will never be considered as an event if r is multi-valued. However, one can always replace this declaration by x.r := delete(x.r, y) which is an event, but which costs a memory allocation for the creation of the new x.r.

In addition, a new event pattern was introduced in CLAIRE 3.0 to capture the transition from an old to a new value. This is achieved with the expression x.r := (z <- y) which says that the last event is the update of x.r from z to y. For instance, here is the event expression that states that x.salary crossed the 100000 limit :
 x.salary := (y <- z& y < 100000 & z >= 100000
In CLAIRE 3.2 we introduce the notion of a "pure" event. If a property p has no restrictions, then p(x,y) represents a virtual call to p with parameters x and y. This event may be used in a rule in a way similar to x.p := y, with the difference that it does not correspond to an update. Virtual events are very generic since one of the parameter may be arbitrarily complex (a list, a set, a tuple ...). The event filter associated to a virtual event is simply the expression "p(x,y)". To create such an event, one simply calls p(x,y), once a rule using such an event has been defined. As a matter of fact, the definition of a rule using p(x,y) as an event pattern will provoke the creation of a generic method p that creates the event.

Virtual event may be used for many purposes. The creation of a virtual event requires no time nor memory; thus, it is a convenient technique to capture state transition in your object system. For instance, we can create an event signaling the instantiation of a class as follows :
 instantiation :: property(domain = myClassrange = string)

 [close(x:MyClass: MyClass -> instantiation(x,date!(1)), x]

 controlRule() :: rule(instantiation(x,s=> printf("--- create ~S at ~A \n",x,s))
To define a rule, we must indeed define :

Here is a classical transitive closure example :
 r1() :: rule(x.friends :add y => for z in y.friend x.friends :add z)
Rules are named (for easier debugging) and can use any CLAIRE expression as a conclusion, using the event parameters as variables. Rule triggering can be traced using trace(if_write). Notice that a rule definition in CLAIRE 3.2 has no parameters; rules with parameters require the presence of the ClaireRules library, which is no longer available.

For instance, let us define the following rule to fill the table fib with the Fibonacci sequence :
 r3() :: rule(y := fib[x& x % (0 .. 100)
         => when z := get(fib,x - 1in fib[x + 1:= y + z)

 (fib[0:= 1fib[1:= 1)


Rules categories
Tables, Rules and Hypothetical Reasoning
Hypothetical Reasoning
I/O, Modules and System Interface
Communication ports

Hypothetical Reasoning

In addition to rules, CLAIRE also provides the ability to do some hypothetical reasoning. It is indeed possible to make hypotheses on part of the knowledge (the database of relations) of CLAIRE, and to change them whenever we come to a dead-end. This possibility to store successive versions of the database and to come back to a previous one is called the world mechanism (each version is called a world). The slots or tables x on which hypothetical reasoning will be done need to be specified with the declaration store(x). For instance :
 store(age,friends,fib<=> store(age), store(friends), store(fib)
Each time we ask CLAIRE to create a new world, CLAIRE saves the status of tables and slots declared with the store command. Worlds are represented with numbers, and creating a new world is done with choice(). Returning to the previous world is done with backtrack(). Returning to a previous world n is done with backtrack(n). Worlds are organized into a stack (sorry, you cannot explore two worlds at the same time) so that save/restore operations are very fast. The current world that is being used can be found with world?(), which returns an integer.

Definition : A world is a virtual copy of the defeasible part of the object database. The object database (set of slots, tables and global variables) is divided into the defeasible part and the stable part using the store declaration. Defeasible means that updates performed to a defeasible relation or variable can be undone later; r is defeasible if store(r) has been declared. Creating a world (choice) means storing the current status of the defeasible database (a delta-storage using the previous world as a reference). Returning to the previous world (backtrack) is just restoring the defeasible database to its previously stored state.

In addition, you may accept the hypothetical changes that you made within a world while removing the world and keeping the changes. This is done with the commit methods. commit() decreases the world counter by one, while keeping the updates that were made in the current world. It can be seen as a collapse of the current world and the previous world. commit(n) repeats commit() until the current world is n. Notice that this "collapse" will simply make the updates that were made in the current world (n) look like they were made in the previous world (n - 1); thus, these updates are still defeasible.

Defeasible updates are fairly optimized in CLAIRE, with an emphasis on minimal book-keeping to ensure better performance. Roughly speaking, CLAIRE stores a pair of pointers for each defeasible update in the world stack. There are (rare) cases where it may be interesting to record more information to avoid overloading the trailing stack. For instance, trailing information is added to the stack for each update even if the current world has not changed. This strategy is actually faster than using a more sophisticated book-keeping, but may yield a world stack overflow.

For instance, here is a simple program that solves the n queens problem (the problem is the following: how many queens can one place on a chessboard so that none are in situation of chess, given that a queen can move vertically, horizontally and diagonally in both ways ?) :
 column[n:(1 .. 8)] : (1 .. 8:= unknown
 possible[x:(1 .. 8), y:(1 .. 8)] : boolean := true
 store(columnpossible)

 r1() :: rule(column[x:= z
             => for y in ((1 .. 8but xpossible[y,z:= false)
 r2() :: rule(column[x:= z
             => let d := x + z
                 in for y in (max(1,d - 8.. min(d - 18))
                     possible[y,d - y:= false )
 r3() :: rule(column[x:= z
             => let d := z - x
                 in for y in (max(1,1 - d.. min(8,8 - d))
                     possible[y,y + d:= false)

 queens(n:(0 .. 8)) : boolean
     -> (if (n = 0true
         else exists(p in (1 .. 8| (possible[n,p&
                     branch((column[n:= pqueens(n - 1))))))

 (queens(8))
In this program queens(n) returns true if it is possible to place n queens. Obviously there can be at most one queen per line, so the purpose is to find a column for each queen in each line : this is represented by the column table. So, we have eight levels of decision in this problem (finding a line for each of the eight queens). The search tree (these imbricated choices) is represented by the stack of the recursive calls to the method queens. At each level of the tree, each time a decision is made (an affectation to the table column), a new world is created, so that we can backtrack (go back to previous decision level) if this hypothesis (this branch of the tree) leads to a failure.

Note that the table possible, which tells us whether the nth queen can be set on the pth line, is filled by means of rules triggered by column (declared event) and that both possible and column are declared store so that the decisions taken in worlds that have been left are undone (this avoids to keep track of decisions taken under hypotheses that have been dismissed since).

Updates on lists can also be "stored" on worlds so that they become defeasible. Instead of using the nth= method, one can use the method store(l,x,v,b) that places the value v in l[x] and stores the update if b is true. In this case, a return to a previous world will restore the previous value of l[x]. If the boolean value is always true, the shorter form store(l,x,y) may be used. Here is a typical use of store :
 store(l,n,y,l[n!= y)
This is often necessary for tables with range list or set. For instance, consider the following :
 A[i:(1 .. 10)] : tuple(integer,integer,integer:= list<integer>(0,0,0)
 (let l := A[x]
 in (l[1:= 3l[3:= 3))
even if store(A) is declared, the manipulation on l will not be recorded by the world mechanism. You would need to write :
 A[x:= list(3A[x][2], 3)
Using store, you can use the original (and more space-efficient) pattern and write :
 (let l := A[x]
 in (store(l,1,3), store(l,3,3)))
There is another problem with the previous definition. The expression given as a default in a table definition is evaluated only once and the value is stored. Thus the same list<integer>(0,0,0) will be used for all A[x]. In this case, which is a default value that will support side-effects, it is better to introduce an explicit initialization of the table :
 (for i in (1 .. 10A[i:= list<integer>(0,0,0))
There are two operations that are supported in a defeasible manner: direct replacement of the ithelement of l with y (using store(l,i,y)) and adding a new element at the end of the list (using store(l,y)). All other operations, such as nth+ or nth- are not defeasible. The addition of a new element is interesting because it either returns a new list or perform a defeasible side-effect. Therefore, one must also make sure that the assignment of the value of store(l,x) is also made in a defeasible manner (e.g., placing the value in a defeasible global variable). To perform an operation like nth+ or delete on a list in a defeasible manner, one usually needs to use an explicit copy (to protect the original list) and store the result using a defeasible update (cf. the second update in the next example).

It is also important to notice that the management of defeasible updates is done at the relation level and not the object level. Suppose that we have the following :
 C1 <: object(a:list<any>, b:integer)
 C2 <: thing(c:C1)
 store(c,a)
 P :: C1()
 P.c := C2(a = list<any>(1,2,3) , b = 0// defeasible but the C2 object remains
 P.c.a := delete(copy(P.c.a), 2// this is defeasible
 P.c.b := 2  // not defeasible
The first two updates are defeasible but the third is not, because store(b) has not been declared. It is also possible to make a defeasible update on a regular property using put_store. It is worth noticing that hypothetical reasoning.


Tables, Rules and Hypothetical Reasoning
Hypothetical Reasoning
categories
I/O, Modules and System Interface
Communication ports
Printing

I/O, Modules and System Interface

Communication ports [XL]

In XL CLAIRE, the entire port interface has been rewritten such port is now the root class for an extensible hierarchy of communication interface (In CLAIRE 3, ports are based on a C++ import). We define two sorts of port :

Definition : A device is a communication port that is connected to a physical port like a file or a socket that can be handled through a chain of filter

Definition : A filter is a communication port that may modify, buffer or look at a data read or written from a device.

Given this sorts, we define the descriptor device as a wrapper for UNIX descriptor which handles read, write (read_port and write_port interface) and close (close_port interface) operations in a unified way for each derived class (disk_file, socket, pipe). At startup, 3 global variables named stdin, stdout and stderr are created to hold the standard input, output, and error devices respectively (UNIX descriptors 0,1,2 on most system).

Languages often provide these standard ports in a buffered way, that is system calls read(2) or write(2) are made by chunks. So XL CLAIRE comes with two kind of filter, the buffer (as created by buffer!) that perform read (or write) once for each read (or written) chunk of a given size and the line_buffer (as created by line_buffer!) that perform write calls once for each written line. Depending on how the program was launched, the standard output may be a terminal or something else (e.g. pipe). In the later case we'll always provide stdout as a buffer but when it is found that the output is a terminal device, which is often shared by multiple processes, we'll provide stdout as a line_buffer. On the other hand the standard error port is always provided unbuffered, such that in case of crash we avoid data miss that could be hold by a buffer.

To avoid problems of synchronization between reading and writing, it is sometimes useful to ensure that the buffer of a given port is empty. This is done by the command flush(p:port). flush(p) will perform all printing instructions for the port p that are waiting in the associated buffer (flush_port interface).

A (buffered) file is opened with fopen(s:string,m:string) where s is the file path and m the opening mode ("r": read, "w": write, "a": append). For instance :
 inefficient_show_size(filepath:string: void ->
     let f := fopen(filepath"r"),
         content := fread(f)
     in printf("File ~A has ~S bytes\n"filepathlength(content))
An other provided interface is the ability to make a port from a string and vice versa. In XL CLAIRE we call that blob (based on device), the internal data representing the string is a chunk of memory allocated dynamically outside CLAIRE memory (CLAIRE 3 port! interface is supported for compatibility). Blob can be made in various ways with blob! including blob!(s:string) that would initialize the internal data with the string s :
 let b := blob!("toto")
 in assert(fread(b= "toto")

 let b := blob!()
 in (fwrite("toto",b),
     assert(fread(b= "toto"))


Communication ports categories
I/O, Modules and System Interface
Printing
WCL syntax

Printing

There are several ways of printing in CLAIRE. Any entity may be printed with the function print. When print is called for an object that does not inherit from thing (an object without a name), it calls the method self_print of which you can define new restrictions whenever you define new classes. If self_print was called on an object x owned by a class toto for which no applicable restriction could be found, it would print <toto>.

In the case of bags (sets or lists), strings, symbols or characters, the standard method is princ. It formats its argument in a somewhat nicer way than print. For example :
 print("john"prints "john"
 princ("john"prints john
Finally, there also exists a printf macro as in C. Its first argument is a string with possible occurrences of the control patterns ~S, ~I and ~A. The macro requires as many arguments as there are "tilde patterns" in the string, and pairs in order of appearance arguments together with tildes. These control patterns do not refer to the type of the corresponding argument but to the way you want it to be printed. The macro will call print for each argument associated with a ~S form, princ for each associated with a ~A form and will print the result of the evaluation of the argument for each ~I form. A mnemonic is A for alphanumeric, S for standard and I for instruction. Hence the command :
 printf("~S is ~A and here is what we know\n ~I"john23show(john))
will be expanded into :
 (print(john), princ(" is "), princ(23),
     princ(" and here is what we know\n"),
         show(john))
Output may also be directed to a file or another device instead of the screen, using a port. A port is an object bound to a physical device, a memory buffer or a file. The method use_as_output is meant to select the port on which the output will be written. Following is an example :
 let p := fopen("agenda-2006""w")
 in (use_as_output(p),
     write(agenda), fclose(p))
[XL] In XL CLAIRE printf construction can take a port argument and would perform a local output rediction to the supplied port :
 printf(my_port"~S is ~A and here is what we know\n ~I"john23show(john))
will be expanded into :
 let old := use_as_output(my_port)
 in (print(john), princ(" is "), princ(23),
     princ(" and here is what we know\n"), show(john),
  use_as_output(old))
CLAIRE also offers a simple method to redirect the output towards a string port. Two methods are needed to do this: print_in_string and end_of_string. print_in_string() starts redirecting all printing statements towards the string being built. end_of_string() returns the string formed by all the printing done between these two instructions. You can only use print_in_string with one output string at a time; more complex uses require the creation of multiple string ports.

All trace statements will be directed to this port. A trace statement is either obtained implicitly through tracing a method or a rule, or explicitly with the trace statement. the statement trace(n, <string>, <args> ...) is equivalent to printf(<string>, <args> ..) with two differences: the string is printed only if the verbosity level verbose() is higher than n and the output port is ctrace(). The following lines are equivalent :
 trace(0"assigning ~S with ~S"xy)
 //[0] assigning ~S with ~S // xy
 (if (verbose() >= 0printf(ctrace(), "assigning ~S with ~S"xy))
[XL] In XL CLAIRE however, trace instructions are bound to a module such one can specify a per module verbose policy : a module m has a slot m.verbose that can take the values :


Printing categories
I/O, Modules and System Interface
WCL syntax
Reading

WCL syntax [XL]

XL CLAIRE comes with a new printing facility called WCL syntax standing for Web CLaire syntax due to its design originally meant for web oriented applications with generation of dynamic content. WCL syntax draws its inspiration from HTML and the ability to embed CLAIRE code in such language. It comes as a printing alternative to printf and also perform inline substitution. For instance, here is a simple WCL fragment and its printf equivalent :
 ?>Hello world<? <=> printf("Hello world")
A WCL fragment is introduced with the keyword ?> which is the beginning of a static string terminated by the corresponding keyword <? substituted at read time by a call to princ. As a convenience a WCL fragment may not be delimited with a coma as shown in the following equivalent forms :
 ?>toto<? princ("titi"?>tata<?
 ?>toto<? princ("titi"),