| AFNIX Programmer's guide

Getting started

This chapter is a quick introduction to the AFNIX Programming Language. AFNIX is a multi-threaded functional programming language with dynamic symbol bindings that supports the object oriented paradigm. The language features a state of the art runtime engine that runs on both 32 and 64 bits platforms. AFNIX is an interpreted language with a rich syntax that makes the functional programming a pleasant activity. When used interactively, commands are entered on the command line and executed when a complete and valid syntactic object has been constructed. Alternatively, the interpreter can execute a source file. The engine does not have a garbage collector but operates with an immediate, scope based, object destruction mechanism.

AFNIX is a comprehensive set of application clients and modules. The original distribution contains the core interpreter with additional clients like the compiler, the librarian and the debugger. The distribution contains also a rich set of modules that are dedicated to a particular domains. The basic modules are the standard i/o module, the system module and the networking module. The engine is written in C++ and provides runtime compatibility with it. Such compatibility includes the ability to instantiate C++ classes, use virtual methods and raise or catch exceptions. A comprehensive programming interface has been designed to ease the integration of foreign libraries.

AFNIX operates with a set of reserved keywords and predicates. Standard objects provide support for integers, real numbers, strings, characters and boolean. Various containers like list, vector, hash table, bitset, and graphs are also available in the core distribution. The language incorporates the concept lambda expression with explicit closure. Symbol scope limitation is an exclusive feature called gamma expression. Form like notation with an easy block declaration is also an extension. The object model provides a single inheritance mechanism with dynamic symbol resolution. Unique features include instance re-parenting, class rebinding and instance inference. Native class derivation and method override is also part of the object model with fixed class objects and forms.

AFNIX implements a true multi-threaded engine with automatic object protection mechanism against concurrent access. Read and write locking system and thread activation via condition objects is also built in the core system. The engine incorporates an original regular expression engine with group matching, exact or partial match and substitution. An advanced exception engine is also provided with native run-time compatibility. AFNIX provides extensions. An extension is a module or an application which is not installed by default. The user selects during the installation process which extension is needed. For example, the static version of the interpreter is an extension.

First programs

The fundamental AFNIX syntactic object is a form. A form is parsed and immediately executed by the AFNIX engine. A form is generally constructed with a function name and a set of arguments. The process of executing a form is called the evaluation. As a simple program, the traditional `"hello world"` program is shown below.

Hello world

The `"hello world"` program is de rigueur when introducing a new programming language. Here is the AFNIX version of it.

AFNIX is an interpreted language. It is possible to invoke the interpreter and enter the above commands, or use a text editor and execute the file. By convention, an AFNIX source file has the extension `.als`. A simple session to run the above program -- assuming the source file is called `hello.als` -- is shown below.

It is also possible to invoke the interpreter as to enter the commands interactively. The result will be the same. Simply typing `ctrl-d` will exit the session. Another way to operate is to call the AFNIX compiler called axc, and then invoke the interpreter with the compiled file, even let the interpreter to figure this out. Note that the interpreter assume the `.axc` for compiled file.

The order of search is determined by a special system called the file resolver. Its behavior is described in a special chapter of this manual.

Interpreter command

The AFNIX interpreter can be invoked with several options, a file to execute and some program arguments. The `-h` option prints the various interpreter options.

The `-v` option prints the interpreter version and operating system. The `-f` option turn on or off some additional options like assertion checking. The program arguments are illustrated later in this chapter. The `-i` option add a path to the interpreter file path resolver. Several `-i` options can be specified. The order of search is determined by the option order. The use of the resolver combined with the librarian is described in a specific chapter. If the initial file name to execute contains a directory path, such path is added automatically to the interpreter resolver path unless the `nopath` option is specified.

Interactive line editing

Line editing capabilities are supported when the interpreter is used interactively. Error messages are displayed in red if the terminal supports colors. The following table is a resume of the default key bindings.

Binding | Description
--- | ---
backspace | Erase the previous character
delete | Erase at the cursor position
insert | Toggle insert with in-place
left | Move the cursor to the left
right | Move the cursor to the right
up | Move up in the history list
down | Move down in the history list
ctrl-a | Move to the beginning of the line
ctrl-e | Move to the end of the line
ctrl-u | Clear the input line
ctrl-k | Clear from the cursor position
ctrl-l | Refresh the line editing

Command line arguments

The command line arguments to the interpreter are stored in a vector called `argv` which is part of the `interp` class. A complete discussion about class data member is covered in the class object chapter. The example below which illustrates the use of the vector argument.

Loading a source file

The interpreter class provides also the `load` method to load a source file. The argument must be a valid file path or an exception is raised. The `load` method returns `nil`. When the file is loaded, the interpreter input, output and error streams are used. The load operation read one form after another and executes them sequentially.

If the file has been compiled, the `axc` extension can be used instead. This force the interpreter to load the compiled version. If you are not sure, or do not care about which file is loaded, the extension can be omitted.

Without extension, the compiled file is searched first. If it is not found the source file is searched and loaded.

The compiler

The client `axc` is the cross compiler. It generates a binary file that can be run across platforms. The `-h` option prints the various compiler options.

One or several files can be specified on the command line. The source file can be searched by the resolver by using the `-i` option.

builtin objects

AFNIX provides several builtin objects, namely `Boolean`, `Integer`, `Real`, `Character` and `String`. A builtin object can be constructed literally for each of these types. The best way to build such object is to bind it to a symbol. The `const` and `trans` reserved keywords are used to declare a new symbol. A symbol is simply a binding between a name and an object. Almost any standard characters can be used to declare a symbol.

None of the symbols -- or names -- used in the program above are reserved keywords. In fact, the capitalize names are builtin objects such like Integer or String. The `const` reserved keyword creates a constant symbol and returns the last evaluated object. As a consequence, nested `const` constructs are possible like `trans b (const a 1)`. The `trans` reserved keyword declare a new non-constant symbol. That is, the symbol can be changed. Note that it is the symbol which is marked constant, not the object.

Comments

Comments starts with the character `#`. All characters until the end of line are consumed. Comments can be placed anywhere in the source file. Comments entered during an interactive session are discarded.

Forms

The previous program was an illustration of the simplest form declaration, referred as implicit form. An implicit form is a single line command. When a command is becoming complex, the use of the standard form notation is more readable. The standard form uses the `(` and `)` characters to start and close a form. The previous programs could have been written with the standard form notation instead of the implicit one. The use of standard form notation versus the implicit is one is a matter of style and readability.

A form causes an evaluation. When a form is evaluated, each symbol in the form are evaluated to their corresponding internal object. Then the interpreter treats the first object of the form as the object to execute and the rest is the argument list for the calling object. The use of form inside a form is the standard way to perform recursive evaluation for complex expression.

The previous program defines a symbol which is initialized with the integer 3, that is the result of the computation `(+ 1 2)`. The program shows also that Polish notation is used for arithmetic. If fact, `+` is a builtin operator which causes the arguments to be summed (if possible). Evaluation can be nested as well as definition and assignation. When a form is evaluated, the result of the evaluation is made available to the calling form. If the result is obtained at the top level, that result is discarded.

This program illustrates the mechanic of the evaluation process. The evaluation is done recursively. The `(+ 1 2)` form is evaluated as 3 and the result transmitted to the form `(trans a 3)`. This form not only creates the symbol `a` and binds to it the integer 3, but returns also 3 which is the result of the previous evaluation. Finally, the form `(const b 3)` is evaluated, that is, the symbol `b` is created and the result discarded. Internally, things are a little more complex, but the idea remains the same. This program illustrates also the usage of the `assert` keyword.

Lambda expression

A lambda expression is a function in the AFNIX terminology. The term come historically from Lisp to express the fact that a lambda expression is analog to the concept of expression found in the lambda calculus. There are various ways to create a lambda expression. A lambda expression is created with the `trans` reserved keywords. A lambda expression takes 0 or more arguments and return an object. A lambda expression is also an object by itself. When a lambda expression is called, the arguments are evaluated from left to right and placed on the interpreter eval stack. The function is then called and the object result is transmitted to the calling form. The use of `trans` vs `const` is explain later. As an example, we define the factorial of an integer in a recursive way.

This program calls for several comments. First the `trans` keyword defines a new function object with one argument called `n`. The body of the function is defined with the `if` reserved keyword and can be easily understood. The function is called in the next form when the `println` reserved keyword is executed. Note that here, the call to `fact` produces an integer object, which is converted automatically by the `println` keyword.

Block form

The notation used in the `fact` program is the standard form notation originating from Lisp and the Scheme dialect. AFNIX offers another notation called the block form notation with the use of the `{` and `}` characters. A block form is a syntactic notation where each form in the block form is executed sequentially. The form can be either an implicit or a regular form. The `fact` procedure can be rewritten with the block notation as illustrated below.

Another way to create a lambda expression is via the reserved keyword `lambda`. Recall that a lambda expression is an object. So when such object is created, it can be bounded to a symbol. The factorial example could be rewritten with an explicit lambda call.

Note that here, the symbol `fact` is a constant symbol. The use of `const` is rather reserved for gamma expression.

Gamma expression

A lambda expression can somehow becomes very slow during the execution, since the symbol evaluation is done within a set of nested call to resolve the symbols. In other words, each recursive call to a function creates a new symbol set which is linked with its parent. When the recursion is becoming deep, so is the path to traverse from the lower set to the top one. Afnix provides another mechanism called gamma expression which binds only the function symbol set to the top level one. The rest remains the same. Using a gamma expression can speedup significantly the execution.

We will come back later to the concept of gamma expression. The use of the reserved keyword `const` to declare a gamma expression makes now sense. Since most function definitions are constant with one level, it was a language choice to implement this syntactic sugar. Note that `gamma` is a reserved keyword and can be used to create a gamma expression object. On the other hand, note that the gamma expression mechanism does not work for instance method. We will illustrate this point later in this book.

Lambda generation

A lambda expression can be used to generate another lambda expression. In other word, a function can generate a function, hence the term that AFNIX is a functional programming language. Suppose one might want to write a function which take an argument and generate a function which add this argument to the generated function argument -- got that! --, then here is the implementation.

The interesting part in the previous program is the concept of closed variables. Looking at the lambda expression inside `gen`, notice that the argument to the gamma is `x` while `n` is marked in a form before the body of the gamma. This notation indicates that the gamma should retain the value of the argument `n` when the closure is created. In the literature, you might discover a similar mechanism referenced as a closure. A closure is simply a variable which is closed under a certain context. When a variable is reference in a context without any definition, such variable is called a free variable. We will see later more programs with closures. Note that in the AFNIX terminology, it is the object created by the gamma call which is called a closure. Note also that the same mechanism apply with lambda. In short, a lambda expression is a function with or without closed variables, which works with nested symbol sets also called namesets. A gamma expression is a function with or without closed variable which is bounded to the top level nameset. The reserved keyword `trans` binds a lambda expression. The reserved keyword `const` binds a gamma expression. A gamma expression cannot be used as an instance method.

Multiple arguments binding

A lambda or gamma expression can be defined to work with extra arguments using the special `args` binding. During a lambda or gamma expression execution, the special symbol `args` is defined with the extra arguments passed at the call. For example, a gamma expression with 0 formal argument and 2 actual arguments has `args` defined as a cons cell.

The symbol `args` can also be defined with formal arguments. In that case, `args` is defined as a cons cell with the remaining actual arguments.

It is an error to specify formal arguments after `args`. Multiple `args` formal definition are not allowed. The symbol `args` can also be defined as a constant argument.

Nameset and bindings

A nameset is a container of bindings between a name and symbolic variable. We use the term symbolic variable to denote any binding between a name and an object. There are various ways to express such bindings. The common one in AFNIX is called a symbol. Another type of binding is an argument. Despite the fact they are different, they share a set of common properties, like being settable. Another point to note is the nature of the nameset. As a matter of fact, AFNIX has various type of namesets. The top level nameset is called a global set and is designed to handle a large number of symbols. In a lambda or gamma expression, the nameset is called a local set and is designed to be fast with a small number of symbols. The moral of this little story is to think always in terms of namesets, no matter how it is implemented. All namesets support the concept of parent binding. When a nameset is created (typically during the execution of a lambda expression), this nameset is linked with its parent one. This means that a symbol lookup is done by traversing all nameset from the bottom to the top and stopping when one is found. In term of AFNIX notation, the current nameset is referenced with the special symbol `.`. The parent nameset is referenced with the special symbol `..`. The top level nameset is referenced with the symbol `...`.

Symbol

A symbol is an object which defines a binding between a name and an object. When a symbol is evaluated, the evaluation process consists in returning the associated object. There are various ways to create or set a symbol, and the different reserved keywords account for the various nature of binding which has to be done depending on the current nameset state. One of the symbol property is to be `const` or not. When a symbol is marked as a constant, it cannot be modified. Note here that it is the symbol which is constant, not the object. A symbol can be created with the reserved keywords `const` or `trans`.

Creating a nameset

A nameset is an object which can be constructed directly by using the object construction notation. Once the object is created, it can be bounded to a symbol. Here is a nameset called `example` in the top level nameset.

Qualified name

In the previous example, a symbol is referenced in a given nameset by using a qualified name like `example:hello`. A qualified name define a path to access a symbol. The use of qualified name is a powerful notation to reference an object in reference to another object. For example, the qualified name `.:hello` refers to the symbol `hello` in the current nameset. The qualified name `...:hello` refers to the symbol `hello` in the top level nameset. There are other use for qualified names, like method call with an instance.

Symbol binding

The `trans` reserved keyword has been shown in all previous example. The reserved keyword `trans` creates or set a symbol in the current nameset. For example, the form `trans a 1` is evaluated as follow. First, a symbol named `a` is searched in the current nameset. At this stage, two situations can occur. If the symbol is found, it is set with the corresponding value. If the symbol is not found, it is created in the current nameset and set. The use of qualified name is also permitted -- and encouraged -- with `trans`. The exact nature of the symbol binding with a qualified name depends on the partial evaluation of the qualified name. For example, `trans example:hello 1` will set or create a symbol binding in reference to the `example` object. If `example` refers to a nameset, the symbol is bound in this nameset. If `example` is a class, `hello` is bounded as a static symbol for that class. In theory, there is no restriction to use `trans` on any object. If the object does not have a symbol binding capability, an exception is raised. For example, if `n` is an integer object, the form `trans n:i 1` will fail. With 3 or 4 arguments, `trans` defines automatically a lambda expression. This notation is a syntactic sugar. The lambda expression is constructed from the argument list and bounded to the specified symbol. The rule used to set or define the symbol are the same as described above.

Constant binding

The `const` reserved keyword is similar to `trans`, except that it creates a constant symbol. Once the symbol is created, it cannot be changed. This constant property is hold by the symbol itself. When trying to set a constant symbol, an exception is raised. The reserved keyword `const` works also with qualified names. The rules described previously are the same. When a partial evaluation is done, the partial object is called to perform a constant binding. If such capability does not exist, an exception is raised. With 3 or 4 arguments, `const` defines automatically a gamma expression. Like `trans` the rule are the same except that the symbol is marked constant.

Arguments

An expression argument is similar to a symbol, except that it is used only with function argument. The concept of binding between a name and an object is still the same, but with an argument, the object is not stored as part of the argument, but rather at another location which is the execution stack. An argument can also be constant. On the other hand, a single argument can have multiple bindings. Such situation is found during the same function call in two different threads. An argument list is part of the lambda or gamma expression declaration. If the argument is defined as a constant argument a sub form notation is used to defined this matter. For example, the `max` gamma expression is given below.

A special symbols named `args` is defined during a lambda or gamma expression evaluation with the remaining arguments passed at the time the call is made. The symbol can be either `nil` or bound to a list of objects.

Control flow

AFNIX provides various reserved keywords which can be seen as standard imperative statements. Such statements are useful to write readable programs but are not necessary the best in terms of efficiency. In most cases, a statement returns the last evaluated object. Most of the statements are control flow statements.

If statement

The `if` reserved keyword takes two or three arguments. The first argument is the boolean condition to check. If the condition evaluates to `true` the second argument is evaluated. The form return the result of such evaluation. If the condition evaluates to `false`, the third argument is evaluated or nil is returned if it does not exist. An interesting example which combines the `if` reserved keyword and a deep recursion is the computation of the Fibonacci sequence.

While statement

The `while` reserved keyword takes 2 or 3 arguments. With 2 arguments, the loop is constructed with a condition and a form. With 3 arguments, the first argument is an initial condition that is executed only once. When an argument acts as a loop condition, the condition evaluate to a boolean. The loop body is executed as long as the boolean condition is true. An interesting example related to integer arithmetic with a `while` loop is the computation of the greatest common divisor or gcd.

Note in this previous example the use of the symbol `=`. The qualified name `u:=` is in fact a method call. Here, the integer `u` is assigned with a value. In this case, the symbol is not changed. It is the object which is muted. In the presence of 3 arguments, the first argument is an initialization condition that is executed only once. In this mode, it is important to note that the loop introduce its own nameset. The loop condition can be used to initialize a local condition variable.

Do statement

The `do` reserved keyword is similar to the `while` reserved keyword, except that the loop condition is evaluated after the body execution. The syntax call is opposite to the `while`. The loop can accept either 2 or 3 arguments. With 2 arguments, the first argument is the loop body and the second argument is the exit loop condition. With 3 arguments, the first argument is the initial condition that is executed only once.

Loop statement

The `loop` reserved keyword is another form of loop. It take four arguments. The first is the initialize form. The second is the exit condition. The third is the step form and the fourth is the form to execute at each loop step. Unlike the `while` and `do` loop, the `loop` statement creates its own nameset, since the initialize condition generally creates new symbol for the loop only.

Switch statement

The `switch` reserved keyword is a condition selector. The first argument is the switch selector. The second argument is a list of various value which can be matched by the switch value. A special symbol called `else` can be used to match any value.

Return statement

The `return` reserved keyword indicates an exceptional condition in the flow of execution within a lambda or gamma expression. When a return is executed, the associated argument is returned and the execution terminates. If `return` is used at the top level, the result is simply discarded.

Eval and protect

The `eval` reserved keyword forces the evaluation of the object argument. The reserved keyword `eval` is typically used in a function body to return a particular symbol value. It can also be used to force the evaluation of a protected object. In many cases, `eval` is more efficient than `return`. The `protect` reserved keyword constructs an object without evaluating it. Typically when used with a form, `protect` return the form itself. It can also be used to prevent a symbol evaluation. When used with a symbol, the symbol object itself is returned.

Note that in the preceding example that the evaluation will return a lambda expression which is evaluated immediately and which return the integer 3.

Assert statement

The `assert` reserved keyword check for equality between the two arguments and abort the execution in case of failure. By default, the assertion checking is turn off, and can be activated with the command option `-f assert`. Needless to say that `assert` is used for debugging purpose.

Block statement

The `block` reserved keyword executes a form in a new local set. The local set is destroyed at the completion of the execution. The `block` reserved keyword returns the value of the last evaluated form. Since a new local set is created, any new symbol created in this nameset is destroyed at the completion of the execution. In other word, the `block` reserved keyword allows the creation of a local scope.

builtin objects

AFNIX provides several builtin objects and builtin operators for arithmetic and logical operations. The `Integer` and `Real` classes are primarily used to manipulate numbers. The `Boolean` class is used to for boolean operations. Other builtin objects include `Character` and `String`. The exact usage of these classes is described in the next chapter.

Arithmetic operations

AFNIX provides various ways to perform arithmetic operations. Most of the operations are done with the `+`, `-`, `*` and `/` operators. Each of these operators works with both integer and real numbers.

Logical operations

The `Boolean` class is used to represent the boolean value `true` and `false`. These last two symbols are builtin in the interpreter as constant symbols. AFNIX provides also some reserved keywords like `not`, `and` and `or`. Their usage is self understandable.

Predicates

A predicate is a function which returns a boolean object. AFNIX provides several predicates to check for some builtin objects. By convention, a predicate terminates with the sequence -p. The `nil-p` predicate is a special predicate which returns true if the object is nil. AFNIX provides a predicate for each builtin objects.

Predicate | Description
--- | ---
nil-p | check nil object
eval-p | check evaluation
real-p | check real object
regex-p | check regex object
string-p | check string object
number-p | check number object
boolean-p | check boolean object
integer-p | check integer object
character-p | check character object

For example, one can write a function which returns `true` if the argument is a number, that is, an integer or a real number.

Predicates for functional and symbolic programming are also builtin into the AFNIX engine.

Predicate | Description
--- | ---
class-p | check class object
thread-p | check thread object
promise-p | check promise object
lexical-p | check lexical object
literal-p | check literal object
closure-p | check closure object
nameset-p | check nameset object
instance-p | check instance object
qualified-p | check qualified object

Finally, for each object, a predicate is also associated. For example, `cons-p` is the predicate for the `Cons` object.

Predicate | Description
--- | ---
cons-p | check a cons object
list-p | check for a list object
queue-p | check a queue object
bitset-p | check a bitset object
vector-p | check a vector object

Another issue related to evaluation, is to decide whether or not an object can be evaluated. The predicate `eval-p` which is a special form is designed to answer this question. Furthermore, the `eval-p` predicate is useful to decide whether or not a symbol is defined or if a qualified name can be evaluated.

Class and Instance

AFNIX provides support for the object oriented programming paradigm. A class in the AFNIX terminology is a nameset which can be bounded automatically when an instance of that class is created. Compared to other language, there is no need to declare the data member for a particular class. Data members are created during the instance construction. A class allows an instance to call function with the instance nameset visible for that function.

Class and members

A class is declared with the reserved keyword `class`. The class acts like a nameset. Functions can be bounded to this class.

Any object can be bounded as a data member, including lambda expressions.

Instances

An instance of a class is created like any builtin object. If a method called `preset` is defined for that class, the method is used as an initializer of that instance.

Instance method

When a lambda expression is bound to the class or the instance, that lambda can be invoked as an instance method. When an instance method is invoked, the instance nameset is set as the parent nameset for that lambda. This is the main reason why a gamma expression cannot be used as an instance method. The instance nameset defines the instance data members and the special symbol `this`.

Miscellaneous features

AFNIX provides several facilities for control flow and exceptional operations. Most of these features are available via the use of reserved keywords.

Iteration

An iteration facility is provided for some objects known as iterable objects. The `Cons`, `List` and `Vector` are typical iterable objects. There are two ways to iterate with these objects. The first method uses the `for` reserved keyword. The second method uses an explicit iterator which can be constructed by the object.

The `for` reserved keyword iterate on both object `u` and `v`. For each iteration, the symbol `x` and `y` are set with their respective object value. In the example above, the result is obtained by summing all intermediate products.

The iteration can be done explicitly by creating an iterator for each vectors and advancing steps by steps.

In the example above, two iterators are constructed for both vectors `u` and `v`. The iteration is done in a `while` loop by invoking the `valid-p` predicate. The `get-object` method returns the object value at the current iterator position.

Exception

An exception is an unexpected change in the execution flow. The AFNIX model for exception is based on a mechanism which throws the exception to be caught by a handler. The mechanism is also designed to be compatible with the native "C++" implementation. An exception is thrown with the reserved keyword `throw`. When an exception is thrown, the normal flow of execution is interrupted and an object used to carry the exception information is created. Such exception object is propagated backward in the call stack until an exception handler catch it. The reserved keyword `try` executes a form and catch an exception if one has been thrown. With one argument, the form is executed and the result is the result of the form execution unless an exception is caught. If an exception is caught, the result is the exception object. If the exception is a native one, the result is nil.

The exception mechanism is also designed to install an exception handler and eventually retrieve some information from the exception object. The reserved symbol `what` can be used to retrieve some exception information.

Delayed evaluation

The AFNIX interpreter provides a special mechanism to delay an evaluation. The reserved keyword `delay` creates a special object called a promise which records the form to be later evaluated. The reserved keyword `force` causes a promise to be evaluated. Subsequent call with `force` will produce the same result.

Regular Expressions

The AFNIX interpreter provides a builtin mechanism for regular expression. A regex is an object which is used to match certain text patterns. Regular expressions are built implicitly by the AFNIX reader wit the use of the `[` and `]` characters.

In the previous example, a regular expression object is bound to the symbol `re`. The `regex` contains two groups. The call to the operator `==` returns `true` if the regex matches the argument string. The `get` method can be used to retrieve the group by index.

Threads

The AFNIX interpreter provides a powerful mechanism which allows the concurrent execution of forms and the synchronization of shared objects. There are two types of threads, namely normal thread and daemon thread. They differ only by the interpreter exit condition. The interpreter will wait until all normal threads are completed. On the other hand, the interpreter will not wait for daemon threads. They are automatically stopped when all normal threads are finished. Normal threads are created with the reserved keyword `launch`, and daemon threads are created with the reserved keyword `daemon`. When threads are used, the interpreter manages automatically the shared objects and protect them against concurrent access.

Form synchronization

Although, AFNIX provides an automatic synchronization mechanism for reading or writing an object, it is sometimes necessary to control the execution flow. There are basically two techniques to do so. First, protect a form from being executed by several threads. Second, wait for one or several threads to complete their task before going to the next execution step. The reserved keyword `sync` can be used to synchronize a form. When a form, is synchronized, the AFNIX engine guarantees that only one thread will execute this form.

The previous example create a gamma expression which make sure that both the error code and error message are printed in one group, when several threads call it.

Thread completion

The other piece of synchronization is the thread completion indicator. The thread descriptor contains a method called `wait` which suspend the calling thread until the thread attached to the descriptor has been completed. If the thread is already completed, the method returns immediately.

This example is taken from the test suites. It checks that a closed variable becomes shared when started in a thread. Note the use of the `wait` method to make sure the thread has completed before checking for the shared flag. It is also worth to note that `wait` is one of the method which guarantees that a thread result is valid. Another use of the `wait` method can be made with a vector of thread descriptors when one wants to wait until all of them have completed.

Condition variable

A condition variable is another mechanism to synchronize several threads. A condition variable is modeled with the `Condvar` object. At construction, the condition variable is initialized to `false`. A thread calling the `wait` method will block until the condition becomes `true`. The `mark` method can be used by a thread to change the state of a condition variable and eventually awake some threads which are blocked on it. The use of condition variable is particularly recommended when one need to make sure a particular thread has been doing a particular task.

Numbers and strings

This chapters covers in detail the builtin objects used to manipulate numbers and strings. First the integer, relatif and real numbers are described. AFNIX offers a broad range of methods for these three objects to support numerical computation. As a second step, string and character objects are described. Many examples show the various operations which can be used as automatic conversion between one type and another. Finally, the boolean object is described. These objects belongs to the class of literal objects, that is objects that have a string representation.

Integer number

The fundamental number representation is the `Integer`. The integer is a 64 bits signed 2's complement number. Even when running with a 32 bits machine, the 64 bits representation is used. If a larger representation is needed, the `Relatif` object might be more appropriate.

Integer format

The default literal format for an integer is the decimal notation. The minus sign (without blank) indicates a negative number. Hexadecimal and binary notations can also be used with prefix `0x` and `0b`. The underscore character can be used to make the notation more readable.

Integer number are constructed from the literal notation or by using an explicit integer instance. The `Integer` class offers standard constructors. The default constructor creates an integer object and initialize it to 0. The other constructors take either an integer, a real number, a character or a string.

When the hexadecimal or binary notation is used, care should be taken to avoid a negative integer. For example, `0x_8000_0000_0000_0000` is the smallest negative number.

Integer arithmetic

Standard arithmetic operators are available as builtin operators. The usual addition `+`, multiplication `*` and division `/` operate with two arguments. The subtraction `-` operates with one or two arguments.

As a builtin object, the `Integer` object offers various methods for builtin arithmetic which directly operates on the object. The following example illustrates these methods.

As a side effect, these methods allows a const symbol to be modified. Since the methods operates on an object, they do not modify the state of the symbol. Such methods are called mutable methods.

Integer comparison

The comparison operators works the same. The only difference is that they always return a `Boolean` result. The comparison operators are namely equal `==`, not equal `!=`, less than `<`, less equal `<=`, greater `>` and greater equal `>=`. These operators take two arguments.

Like the arithmetic methods, the comparison operators are supported as object methods. These methods return a `Boolean` object.

Integer calculus

Armed with all these functions, it is possible to develop a battery of functions operating with numbers. As another example, we revisit the Fibonacci sequence as demonstrated in the introduction chapter. Such example was terribly slow, because of the double recursion. Another method suggested by Springer and Friedman uses two functions to perform the same job.

This later example is by far much faster, since it uses only one recursion. Although, it is no the fastest way to write it, but nobody is going to question the elegant aspect of recursion.

Other Integer methods

The `Integer` class offers other convenient methods. The `odd-p` and `even-p` are predicates. The `mod` take one argument and returns the modulo between the calling integer and the argument. The `to-string` method returns a string representation of the integer. The `abs` methods returns the absolute value of the calling integer.

Relatif number

A relatif or big-num is an integer with infinite precision. The `Relatif` class is similar to the `Integer` class except that it works with infinitely long number. The relatif notation uses a `r` or `R` suffix to express a relatif number versus an integer one.

Relatif operations

Most of the `Intege` class operations are supported by the `Relatif` object. The only difference is that there is no limitation on the number size. This naturally comes with a computational price. An amazing example is to compute the biggest know prime Mersenne number. The world record exponent is 6972593. The number is therefore:

This number has 2098960 digits. You can use the `println` method if you wish, but you have been warned...

Real number

The `real` class implements the representation for floating point number. The internal representation is machine dependent, and generally follows the double representation with 64 bits as specified by the IEEE 754-1985 standard for binary floating point arithmetic. All integer operations are supported for real numbers.

Real format

The AFNIX reader supports two types of literal representation for real number. The first representation is the dotted decimal notation. The second notation is the scientific notation.

Real number are constructed from the literal notation or by using an explicit real instance. The `Real` class offers standard constructors. The default constructor creates a real number object and initialize it to 0.0. The other constructors takes either an integer, a real number, a character or a string.

Real arithmetic

The real arithmetic is similar to the integer one. When an integer is added to a real number, that number is automatically converted to a real and vice versa. Ultimately, a pure integer operation might generate a real result.

Like the `Integer` object, the `Real` object has arithmetic builtin methods.

Real comparison

The comparison operators works as the integer one. As for the other operators, an implicit conversion between an integer to a real is done automatically.

Comparison methods are also available for the `Real` object. These methods take either an integer or a real as argument.

A complex example

One of the most interesting point with functional programming language is the ability to create complex computation function. For example, let's assume we wish to compute the value at a point `x` of the Legendre polynomial of order n. One of the solution is to encode the function given its order. Another solution is to compute the function and then compute the value.

Note that the computation can be done either with integer or real numbers. With integers, you might get some strange results anyway, but it will work. Note also how the closed variable mechanism is used. The recursion capture each level of the polynom until it is constructed. As an exercise, try to use a lambda expression instead of a gamma one, and compare the execution result with large number of `n`. Note also that we have here a double recursion.

Other real methods

The real numbers are delivered with a battery of functions. These include the trigonometric functions, the logarithm and couple others. Hyperbolic functions like `sinh`, `cosh`, `tanh`, `asinh`, `acosh` and `atanh` are also supported. The square root `sqrt` method return the square root of the calling real. The `floor` and `ceiling` returns respectively the floor and the ceiling of the calling real.

Accuracy and formatting

Real numbers are not necessarily accurate, nor precise. The accuracy and precision are highly dependent on the hardware as well as the nature of the operation being performed. In any case, never assume that a real value is an exact one. Most of the time, a real comparison will fail, even if the numbers are very close together. When comparing real numbers, it is preferable to use the `?=` operator. Such operator result is bounded by the internal precision representation and will generally return the desired value. The real precision is an interpreter value which is set with the `set-epsilon` method while the `get-epsilon` returns the interpreter precision. Note also that the `Real` object bind the data member `EPSILON`, which can be use with the qualified name `Real:EPSILON`. By default, the precision is set to 0.00001.

Real number formatting is another story. The `format` method takes a precision argument which indicates the number of digits to print for the decimal part. Note that the format command might round the result as indicated in the example below.

If additional formatting is needed, the `String` `fill-left` and `fill-right` methods can be used.

Character

The `Character` object is another builtin object of the AFNIX engine. A character is internally represented by a quad by using a 31 bit representation as specified by the Unicode standard and ISO 10646.

Character format

The standard quote notation is used to represent a character. In that respect, AFNIX differs substantially from other functional language where the quote protect a form.

All characters from the Unicode codeset are supported by the AFNIX engine. The characters are constructed from the literal notation or by using an explicit character instance. The `Character` class offers standard constructors. The default constructor creates a null character. The other constructors take either an integer, a character or a string. The string can be either a single quoted character or the literal notation based on the `U+` notation in hexadecimal. For example, `U+40` is the @ character while `U+3A3` is the greek sigma capital letter Σ.

Character arithmetic

A character is like an integer, except that it operates in the range 0 to 0x7FFFFFFF. The character arithmetic is simpler compared to the integer one and no overflow or underflow checking is done. Note that the arithmetic operations take an integer as an argument.

Several `Character` object methods are also provided for arithmetic operations in a way similar to the `Integer` class.

Character comparison

Comparison operators are also working with the `Character` object. The standard operators are namely equal `==`, not equal `!=`, less than `<`, less equal `<=`, greater `>` and greater equal `>=`. These operators take two arguments.

Other character methods

The `Character` object comes with additional methods. These are mostly conversion methods and predicates. The `to-string` method returns a string representation of the calling character. The `to-integer` method returns an integer representation the calling character. The predicates are `alpha-p`, `digit-p`, `blank-p`, `eol-p`, `eof-p` and `nil-p`.

String

The `String` object is one of the most important builtin object in the AFNIX engine. Internally, a string is a vector of Unicode characters. Because a string operates with Unicode characters, care should be taken when using composing characters.

String format

The standard double quote notation is used to represent literally a string. Standard escape sequences are also accepted to construct a string.

Any literal object can be used to construct a string. This means that integer, real, boolean or character objects are all valid to construct strings. The default constructor creates a null string. The string constructor can also takes a string.

String operations

With strings, numerous methods can be provided. We illustrate here the most common one.

The `sub-left` and `sub-right` methods return a sub-string, given the position index. For `sub-left`, the index is the terminating index, while `sub-right` is the starting index, counting from 0.

The `strip`, `strip-left` and `strip-right` are methods used to strip blanks and tabs. The `strip` method combines both `strip-left` and `strip-right`. The `split` method returns a vector of strings by splitting the string according to a break sequence. By default, the break sequence is the blank, tab and newline characters. The break sequence can be one or more characters passed as one single argument to the method.

The `fill-left` and `fill-right` methods can be used to fill a string with a character up to a certain length. If the string is longer than the length, nothing happens.

String hash value

Computing the hash value of a string is an interesting problem. The algorithm used by the AFNIX engine is shown as an example below. Note that the `hashid` method is built in the `String` object. The program shows both internal and computed values.

When run, example 0203.als, the following result is obtained with a 32 bits machine.

As a side note, it is recommended to print the shift amount in the program. One may notice, that the value remains bounded by 24. Since we are "xoring" the final value, it does illustrate that the algorithm is design for a 32 bits machine. With a 64 bits machine the algorithm is slightly modified to use the extra space. This also means that the hashid value is not portable across platforms.

Container objects

This chapter covers the builtin container objects and more specifically, iterable objects such like `Cons`, `List` and `Vector`. Special objects like `Queue` and `Bitset` are mentioned at the end.

Cons builtin object

Originally, a `Cons` object or cons cell have been the fundamental object of the Lisp or Scheme machine. The cons cell is the building block for list and is of great importance in AFNIX as well. A `Cons` object is a simple element used to build linked list. The cons cell holds an object and a pointer to the next cons cell. The cons cell object is called car and the next cons cell is called the cdr. This notation, found in Lisp in maintained here for the sake of tradition.

Cons cell constructors

The default constructor creates a cons cell those car is initialized to the nil object. The constructor can also take one or several objects.

The constructor can take any kind of objects. When all objects have the same type, the result list is said to be homogeneous. If all objects do not have the same type, the result list is said to be heterogeneous. List can also be constructed directly from the AFNIX reader. Since all internal forms are built with cons cell, the construction can be achieved by simply protecting the form from being interpreted.

Cons cell methods

A `Cons` object provides several methods to access the car and the cdr of a cons cell. Other methods allows access to a list by index.

The `set-car` method set the car of the cons cell. The `append` method appends a new cons cell at the end of the cons list and set the car with the specified object.

List builtin object

The `List` builtin object provides the facility of a double-link list. The `List` object is another example of iterable object. The `List` object provides support for forward and backward iteration.

List construction

A list is constructed like a cons cell with zero or more arguments. Unlike the cons cell, the `List` can have a null size.

List methods

The `List` object methods are similar the `Cons` object. The `append` method appends an object at the end of the list. The `insert` method inserts an object at the beginning of the list.

Vector builtin object

The `Vector` builtin object provides the facility of an index array of objects. The `Vector` object is another example of iterable object. The `Vector` object provides support for forward and backward iteration.

Vector construction

A vector is constructed like a cons cell or a list. The default constructor creates a vector with 0 objects.

Vector methods

The `Vector` object methods are similar to the `List` object. The `append` method appends an object at the end of the vector. The `set` method set a vector position by index.

Set builtin object

The `Set` builtin object provides the facility of an object container. The `Set` object is another example of iterable object. The `Set` object provides support for forward iteration. One of the property of a set is that there is only one object representation per set. Adding two times the same object results in one object only.

Set construction

A set is constructed like a vector. The default constructor creates a set with 0 objects.

Set methods

The `Set` object methods are similar to the `Vector` object. The `add` method adds an object in the set. If the object is already in the set, the object is not added. The `length` method returns the number of elements in the set.

Iteration

When an object is iterable, it can be used with the reserved keyword `for`. The `for` keyword iterates on one or several objects and binds associated symbols during each step of the iteration process. All iterable objects provides also the method `get-iterator` which returns an iterator for a given object. The use of iterator is justified during backward iteration, since `for` only perform forward iteration.

Function mapping

Given a function `func`, it is relatively easy to apply this function to all objects of an iterable object. The result is a list of successive calls with the function. Such function is called a mapping function and is generally called `map`.

The `link` method differs from the `append` method in the sense that the object to append is set to the cons cell car if the car and cdr is nil.

Multiple iteration

Multiple iteration can be done with one call to `for`. The computation of a scalar product is a simple but illustrative example.

Note that the function `scalar-product` does not make any assumption about the object to iterate. One could compute the scalar product between a vector a list for example.

Conversion of iterable objects

The use of an iterator is suitable for direct conversion between one object and another. The conversion to a vector can be simply defined as indicted below.

Explicit iterator

An explicit iterator is constructed with the `get-iterator` method. At construction, the iterator is reset to the beginning position. The `get-object` method returns the object at the current iterator position. The `next` advances the iterator to its next position. The `valid-p` method returns `true` if the iterator is in a valid position. When the iterator supports backward operations, the `prev` method move the iterator to the previous position. Note that `Cons` objects do not support backward iteration. The `begin` method reset the iterator to the beginning. The `end` method moves the iterator the last position. This method is available only with backward iterator.

Special Objects

The AFNIX engine provides several builtin container objects which are special case of container objects. Such objects are `Queue` and `Bitset`

Queue object

A queue is a special object which acts as container with a FIFO policy. When an object is placed in the queue, it remains there until it has been dequeued.

BitSet object

A bit set is a special container for bit. A bit set can be constructed with a specific size. When the bit set is constructed, each bit can be marked and tested by index.

Classes

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

Class object

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

Class declaration and bindings

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

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

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

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

Class closure binding

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

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

Class symbol access

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

Instance

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

Instance construction

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

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

Instance initialization

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

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

Initialization with data member list

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

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

Instance symbol access

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

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

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

Instance method

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

Instance operators

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

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

Complex number example

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

Inheritance

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

Derivation construction

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

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

Derived symbol access

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

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

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

Instance re-parenting

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

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

Instance re-binding

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

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

Instance inference

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

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

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

Advanced concepts

This chapter covers advanced concepts of the AFNIX programming language. The first subject is the exception model. The second subject covers some properties of the namesets. Finally, the interpreter object is described in details.

Exception

An exception is an unexpected change in the execution flow. The AFNIX model for exception is based on a mechanism which throws the exception to be caught by a handler. The mechanism is also designed to be compatible with the native "C++" implementation.

Throwing an exception

An exception is thrown with the reserved keyword `throw`. When an exception is thrown, the normal flow of execution is interrupted and an object used to carry the exception information is created. Such exception object is propagated backward in the call stack until an exception handler catch it.

The example above is the general form to throw an exception. The first argument is the the exception id. The second argument is the exception reason. The third argument is the exception object. The exception id and reason are always a string. The exception object can be any object which is carried by the exception. The reserved keyword `throw` accepts 0 or more arguments.

With 0 argument, the exception is thrown with the exception id set to "user-exception". With one argument, the argument is the exception id. With 2 arguments, the exception id and reason are set. Within a try block, an exception can be thrown again by using the exception object represented with the `what` symbol.

Exception handler

The reserved keyword `try` executes a form and catch an exception if one has been thrown. With one argument, the form is executed and the result is the result of the form execution unless an exception is caught. If an exception is caught, the result is the exception object. If the exception is a native one, the result is nil.

In its second form, the `try` reserved keyword can accept a second form which is executed when an exception is caught. When an exception is caught, a new nameset is created and the special symbol `what` is bounded with the exception object. In such environment, the exception can be evaluated. The `what:eid` qualified name is the exception id. The `what:reason` qualified name is the exception reason and `what:object` is the exception object.

Exceptions are useful to notify abruptly that something went wrong. With an untyped language like AFNIX, it is also a convenient mechanism to abort an expression call if some arguments do not match the expected types.

Nameset

A nameset is created with the reserved keyword `nameset`. Without argument, the `nameset` reserved keyword creates a nameset without setting its parent. With one argument, a nameset is created and the parent set with the argument.

Default namesets

When a nameset is created, the symbol `.` is automatically created and bound to the newly created nameset. If a parent nameset exists, the symbol `..` is also automatically created. The use of the current nameset is a useful notation to resolve a particular name given a hierarchy of namesets.

Nameset and inheritance

When a nameset is set as the super object of an instance, some interesting results are obtained. Because symbols are resolved in the nameset hierarchy, there is no limitation to use a nameset to simulate a kind of multiple inheritance. The following example illustrates this point.

Delayed Evaluation

The AFNIX engine provides a mechanism called delayed evaluation. Such mechanism permits the encapsulation of a form to be evaluated inside an object called a promise.

Creating a promise

The reserved keyword `delay` creates a promise. When the promise is created, the associated object is not evaluated. This means that the promise evaluates to itself.

The previous example creates a promise and store the argument form. The form is not yet evaluated. As a consequence, the symbol `a` evaluates to the promise object.

Forcing a promise

The reserved keyword `force` the evaluation of a promise. Once the promise has been forced, any further call will produce the same result. Note also that, at this stage, the promise evaluates to the evaluated form.

Enumeration

Enumeration, that is, named constant bound to an object, can be declared with the reserved keyword `enum`. The enumeration is built with a list of literal and evaluated as is.

The complete enumeration evaluates to an `Enum` object. Once built, enumeration item evaluates by literal and returns an `Item` object.

Items are comparable objects. Only items can be compared. For a given item, the source enumeration can be obtained with the `get-enum` method.

Logger

The Looger class is a message logger that stores messages in a buffer with a level. The default level is the level 0. A negative level generally indicates a warning or an error message but this is just a convention which is not enforced by the class. A high level generally indicates a less important message. The messages are stored in a circular buffer. When the logger is full, a new message replace the oldest one. By default, the logger is initialized with a 256 messages capacity that can be resized.

When a message is added, the message is stored with a timestamp and a level. The timestamp is used later to format a message. The `length` method returns the number of logged messages. The `get-message` method returns a message by index. Because the system operates with a circular buffer, the `get-message` method manages the indexes in such way that the old messages are accessible with the oldest index. For example, even after a buffer circulation, the index 0 will point to the oldest message. The `get-message-level` returns the message level and the `get-message-time` returns the message posted time.

In term of usage, the logger facility can be conveniently used with other derived classes. The standard i/o module provides several classes that permits to manage logging operations in a convenient way.

Interpreter

The AFNIX interpreter is by itself a special object with specialized methods which do not have equivalent using the standard AFNIX notation. The interpreter is always referred with the special symbol `interp`. The following table is a summary of the symbols and methods bound to the interpreter.

Symbol | Description
--- | ---
argv | Command arguments vector
os-name | Operating system name
os-type | Operating system type
version | Full afnix version
program-name | Interpreter program name
major-version | Major version number
minor-version | Minor version number
patch-version | Patch version number
afnix-uri | Official uri name
load | Load a file and execute it
launch | Launch a normal thread
daemon | Launch a daemon thread
library | Load and initialize a library
set-epsilon | Set real number precision
get-epsilon | Set real number precision

Arguments vector

The `interp:argv` qualified name evaluates to a vector of strings. Each argument is stored in the vector during the interpreter initialization.

Interpreter version

Several symbols can be used to track the interpreter version and the operating system. The full version is bound to the `interp:version` qualified name. The full version is composed of the major, minor and patch number. The operating system name is bound to the qualified name `interp:os-name`. The operating system type is bound to the `interp:os-type`.

File loading

The `interp:load` method loads and execute a file. The interpreter interactive command session is suspended during the execution of the file. In case of error or if an exception is raised, the file execution is terminated. The process used to load a file is governed by the file resolver. Without extension, a compiled file is searched first and if not found a source file is searched.

Library loading

The `interp:library` method loads and initializes a library. The interpreter maintains a list of opened library. Multiple execution of this method for the same library does nothing. The method returns the library object.

Interpreter duplication

The interpreter can be duplicated with the help of the `dup` method. Without argument, a clone of the current interpreter is made and a terminal object is attached to it. When used in conjunction with the `roll` method, this approach permits to create an interactive interpreter. The `dup` method also accepts a terminal object.

Interpreter loop

The interpreter loop can be run with the `roll`. The loop operates by reading the interpreter input stream. If the interpreter has been cloned with the help of the `dup` method, this method provides a convenient way to operate in interactive mode. The method is not called loop because it is a reserved keyword and starting a loop is like having the ball rolling.

Threads operations

This chapter covers the threads facilities builtin in the AFNIX interpreter. The thread subsystem allows for the execution of concurrent forms with an automatic synchronization mechanism. Designing a good program with concurrent execution is a difficult task. It takes a while to get used with the various synchronization mechanisms which ensure a safe execution, that is no race condition or dead lock. Fortunately, AFNIX provides some unique features that should ease such design.

Normal and daemon threads

The interpreter supports two types of threads, called normal and daemon threads. A normal thread is started with the reserved keyword `launch`. A daemon thread is started with the reserved keyword `daemon`. The difference between a normal thread and a daemon thread is only in the termination of the interpreter. An AFNIX program is completed when all normal threads have terminated. This means that the master thread (i.e the first thread) is suspended until all normal threads have been executed. With daemon threads, the master thread terminates even if some daemon threads are still running.

Starting a normal thread

A normal thread is started with the reserved keyword `launch`. The form to execute in a thread is the argument. The simplest thread to execute is the `nil` thread.

Even the `nil` thread does nothing in term of computation, it does a lot of things internally by turning on the shared objects subsystem.

Thread object and result

When a thread terminate, the thread object holds the result of the last executed form. The thread object is returned by the `launch` or `daemon` command. The `thread-p` predicates returns `true` if the object is a thread descriptor. The thread type can be check with the `normal-p` or `daemon-p` predicates.

The member data `result` of the thread object holds the result of the thread. Although the result can be accessed at any time, the returned value will be nil until the thread as completed its execution.

Although the AFNIX engine will ensure that the result is `nil` until the thread has completed its execution, it does not mean that it is a reliable approach to test until the result is not `nil`. The engine provides various mechanisms to synchronize a thread and eventually wait for its completion.

Shared objects

The whole purpose of using a multi-threaded environment is to provide a concurrent execution with some shared variables. Although, several threads can execute concurrently without sharing data, the most common situation is that one or more global variable are accessed -- and even changed -- by one or more threads. Various scenarios are possible. For example, a variable is changed by one thread, the other thread just read its value. Another scenario is one read, multiple write, or even more complicated, multiple read and multiple write. In any case, the interpreter subsystem must ensure that each objects are in a good state when such operation do occur. The AFNIX engine provides an automatic synchronization mechanism for global objects, where only one thread can modify an object, but several thread can read it. This mechanism known as read-write locking guarantees that there is only one writer, but eventually multiple reader. When a thread start to modify an object, no other thread are allowed to read or write this object until the transaction has been completed. On the opposite, no thread is allowed to change (i.e. write) an object, until all thread which access (i.e. read) the object value have completed the transaction. Because a context switch can occur at any time, the object read-write locking will ensure a safe protection during each concurrent access. Shared objects can be very complicated to detect. For example, if a vector is shared by various threads, the engine will make sure that all vector objects are also shared. A closed variable in a lambda or gamma expression is another example of potential shared object. Executing such lambda form in a thread will automatically mark the closed variables as shared objects. Additionally, when the thread system is started, all object in the global nameset are marked shared.

Shared object predicate

The object predicate method `shared-p` returns true if an object is shared. Since all global objects are marked shared as soon as the thread system is turned on, the following example shows how a nil thread marks a shared variable.

When an object is marked shared, it will remain in this state for rest of the session. Note that when an object is copied (by copy construction), the shared state is not copied. The copied object will become shared depending on its surrounding context. Such context can be a nameset or any other type of container which is shared or not.

Shared protection access

We illustrate the previous discussion with an interesting example and some variations around it. Let's consider a form which increase an integer object and another form which decrease the same integer object. If the integer is initialized to 0, and the two forms run in two separate threads, we might expect to see the value bounded by the time allocated for each thread. In other word, this simple example is a very good illustration of your machine scheduler.

In the previous example, `var` is initialized to 0. The `incr` thread increments `var` while the `decr` thread decrements `var`. Depending on the operating system, the result stays bounded within a certain range. The previous example can be changed by using the main thread or a third thread to print the variable value. The end result is the same, except that there is more threads competing for the shared variable.

Synchronization

Although, AFNIX provides an automatic synchronization mechanism for reading or writing an object, it is sometimes necessary to control the execution flow. There are basically two techniques to do so. First, protect a form from being executed by several threads. Second, wait for one or several threads to complete their task before going to the next execution step.

Form synchronization

The reserved keyword `sync` can be used to synchronize a form. When a form, is synchronized, the AFNIX engine guarantees that only one thread will execute this form.

The previous example create a gamma expression which make sure that both the error code and error message are printed in one group, when several threads call it.

Thread completion

The other piece of synchronization is the thread completion indicator. The thread descriptor contains a method called `wait` which suspend the calling thread until the thread attached to the descriptor has been completed. If the thread is already completed, the method returns immediately.

This example is taken from the test suites. It checks that a closed variable becomes shared when started in a thread. Note the use of the `wait` method to make sure the thread has completed before checking for the shared flag. It is also worth to note that `wait` is one of the method which guarantees that a thread result is valid. Another use of the `wait` method can be made with a vector of thread descriptors when one wants to wait until all of them have completed.

Complete example

We illustrate the previous discussion with a complete example. The idea is to perform a matrix multiplication. A thread is launched when when multiplying one line with one column. The result is stored in the thread descriptor. A vector of thread descriptor is used to store the result.

Condition variable

A condition variable is another mechanism to synchronize several threads. A condition variable is modeled with the `Condvar` object. At construction, the condition variable is initialized to `false`. A thread calling the `wait` method will block until the condition becomes `true`. The `mark` method can be used by a thread to change the state of a condition variable and eventually awake some threads which are blocked on it. The following example shows how the main thread blocks until another change the state of the condition.

In this example, the condition variable is created at the beginning. The thread is started and the main thread blocks until the thread change the state of the condition variable. It is important to note the use of the `wait-unlock` method. When the main thread is re-started (after the condition variable has been marked), the main thread owns the lock associated with the condition variable. The `wait-unlock` method unlocks that lock when the main thread is restarted. Note also that the `wait-unlock` method reset the condition variable. if the `wait` method was used instead of `wait-unlock` the lock would still be owned by the main thread. Any attempt by other thread to call the mark method would result in the calling thread to block until the lock is released. The `Condvar` class has several methods which can be used to control the behavior of the condition variable. Most of them are related to lock control. The `reset` method reset the condition variable. The `lock` and `unlock` control the condition variable locking. The `mark`, `wait` and `wait-unlock` method controls the synchronization among several threads.

Regular expressions

This chapter covers the AFNIX regular expressions or regex syntax and programming use. The AFNIX regex is an original implementation with its own syntax and execution model.

Regular expression syntax

AFNIX implements a regular expression engine via a special `Regex` object. A regular expression can be built implicitly or explicitly with the use of the `Regex` object. The regex syntax uses the `[` and `]` characters as block delimiters. When used in a source file, the lexical analyzer automatically recognizes a regex and built the object accordingly. In other word, the regex system is builtin in the AFNIX language. The following example shows two equivalent way to define the same regex expression.

In its first form, the `[` and `]` characters are used as syntax delimiters. The lexical analyzer automatically recognizes this token as a regex and built the equivalent `Regex` object. The second form is the explicit construction of the `Regex` object. Note also that the `[` and `]` characters are also used as regex block delimiters.

Regex characters and meta-characters

Any character, except the one used as operators can be used in a regex. The `$` character is used as a meta-character -- or control character -- to represent a particular set of characters. For example, `[hello world]` is a regex which match only the `"hello world"` string. The `[$d+]` regex matches one or more digits. The following meta characters are builtin in the regex engine.

Character | Description
--- | ---
$a | matches any letter or digit
$b | matches any blank characters
$d | matches any digit
$e | matches eol, cr and eof
$l | matches any lower case letter
$n | matches eol or cr
$s | matches any letter
$u | matches any upper case letter
$v | matches any valid afnix constituent
$w | matches any word constituent
$x | matches any hexadecimal characters

The uppercase version is the complement of the corresponding lowercase character set.

Character | Description
--- | ---
$A | any character except letter or digit
$B | any character except blank characters
$D | any character except digit
$E | any character except eol, cr and eof
$L | any character except lower case letter
$N | any character except eol or cr
$S | any character except letter
$U | any character except upper case letter
$V | any character except afnix constituent
$W | any character except word constituent
$X | any character except hex characters

A character which follows a `$` character and that is not a meta character is treated as a normal character. For example `$[` is the `[` character. A quoted string can be used to define character matching which could otherwise be interpreted as control characters or operator. A quoted string also interprets standard escaped sequences but not meta characters.

Regex character set

A character set is defined with the `<` and `>` characters. Any enclosed character defines a character set. Note that meta characters are also interpreted inside a character set. For example, `<$d+->` represents any digit or a plus or minus. If the first character is the `^` character in the character set, the character set is complemented with regards to its definition.

Regex blocks and operators

The `[` and `]` characters are the regex sub-expressions delimiters. When used at the top level of a regex definition, they can identify an implicit object. Their use at the top level for explicit construction is optional. The following example is strictly equivalent.

Sub-expressions can be nested -- that's their role -- and combined with operators. There is no limit in the nesting level.

The following unary operators can be used with single character, control characters and sub-expressions.

Operator | Description
--- | ---
* | match 0 or more times
+ | match 1 or more times
? | match 0 or 1 time
| | alternation

Alternation is an operator which work with a secondary expression. Care should be taken when writing the right sub-expression. For example the following regex `[$d|hello]` is equivalent to `[[$d|h]ello]`. In other word, the minimal first sub-expression is used when compiling the regex.

Grouping

Groups of sub-expressions are created with the `(` and `)` characters. When a group is matched, the resulting sub-string is placed on a stack and can be used later. In this respect, the regex engine can be used to extract sub-strings. The following example extracts the month, day and year from a particular date format: `[($d$d):($d$d):($d$d$d$d)]`. This regex assumes a date in the form mm:dd:yyyy.

Grouping is the mechanism to retrieve sub-strings when a match is successful. If the regex is bound to a symbol, the `get` method can be used to get the sub-string by index.

Regex object

Although a regex can be built implicitly, the `Regex` object can also be used to build a new regex. The argument is a string which is compiled during the object construction.

Literal object

A `Regex` object is a literal object. This means that the `to-string` method is available and that a call to the `println` special form will work directly.

Regex operators

The `==` and `!=` operators are the primary operators to perform a regex match. The `==` operator returns `true` if the regex matches the string argument from the beginning to the end of string. Such operator implies the begin and end of string anchoring. The `<` operator returns true if the regex matches the string or a substring or the string argument.

Regex methods

The primary regex method is the `get` method which returns by index the sub-string when a group has been matched. The `length` method returns the number of group match.

The `match` method returns the first string which is matched by the regex.

The `replace` method any occurrence of the matching string with the string argument.

Argument conversion

The use of the `Regex` operators implies that the arguments are evaluated as literal object. For this reason, an implicit string conversion is made during such operator call. For example, passing the integer `12` or the string `"12"` is strictly equivalent. Care should be taken when using this implicit conversion with real numbers.

Functional programming

This chapter covers the interesting aspects of AFNIX with respect to the functional programming paradigm. Functional programming is often described as the ability to create functions that creates functions. As a matter of fact, it is a far bigger subject that finds its root in the lambda calculus. A language -- like AFNIX -- that supports the functional programming paradigm is also sometimes called a high order language.

Function expression

A lambda expression or a gamma expression can be seen like a function object with no name. During the evaluation process, the expression object is evaluated as well as the arguments -- from left to right -- and a result is produced by applying those arguments to the function object. An expression can be built dynamically as part of the evaluation process.

The difference between a lambda expression and a gamma expression is only in the nameset binding during the evaluation process. The lambda expression nameset is linked with the calling one, while the gamma expression nameset is linked with the top level nameset. The use of gamma expression is particularly interesting with recursive functions as it can generate a significant execution speedup. The previous example will behaves the same with a gamma expression.

Self reference

When combining a function expression with recursion, the need for the function to call itself is becoming a problem since that function expression does not have a name. For this reason, AFNIX provides the reserved keyword `self` that is a reference to the function expression. We illustrate this capability with the well-known factorial expression written in pure functional style.

The use of a gamma expression versus a lambda expression is a matter of speed. Since the gamma expression does not have free variables, the symbol resolution is not a concern here.

Closed variables

One of the AFNIX characteristic is the treatment of free variables. A variable is said to be free if it is not bound in the expression environment or its children at the time of the symbol resolution. For example, the expression `((lambda (n) (+ n x)) 1)` computes the sum of the argument `n` with the free variable `x`. The evaluation will succeeds if `x` is defined in one of the parent environment. Actually this example can also illustrates the difference between a lambda expression and a gamma expression. Let's consider the following forms.

The gamma expression `do-print` will produce `3` since it sums the argument `n` bound to 1, with the free variable `x` which is defined in the calling environment as `2`. Now if we rewrite the previous example with a gamma expression the result will be one, since the expression parent will be the top level environment that defines `x` as 1.

With this example, it is easy to see that there is a need to be able to determine a particular symbol value during the expression construction. Doing so is called closing a variable. Closing a variable is a mechanism that binds into the expression a particular symbol with a value and such symbol is called a closed variable, since its value is closed under the current environment evaluation. For example, the previous example can be rewritten to close the symbol `x`.

Note that the list of closed variable immediately follow the argument list. In this particular case, the function `do-print` will print `3` since `x` has been closed with the value `2` has defined in the function `do-print`.

Dynamic binding

Because AFNIX has a dynamic binding symbol resolution, it is possible to have under some circumstances a free or closed variable. This kind of situation can happen when a particular symbol is defined under a condition.

With this example, the symbol `x` is a free variable if the argument `n` is greater than 1. While this mechanism can be powerful, extreme caution should be made when using such feature. Note also that many other language do not allow this kind of behavior. That kind of restriction is primarily driven by the need to have a language with `static binding`. The bad news is that it is impossible to write a compiler with dynamic symbol binding.

Functional objects

Everything in AFNIX is an object. As a consequence, an object can be manipulated, even if it is lexical element, a symbol or a closure.

Lexical and qualified names

The basic forms elements are the lexical and qualified names. Lexical and qualified names are constructed by the AFNIX reader. Although the evaluation process make that lexical object transparent, it is possible to manipulate them directly.

In this example, the `protect` reserved keyword is used to avoid the evaluation of the lexical object named `lex`. Therefore the symbol `sym` refers to a lexical object. Since a lexical -- and a qualified -- object is a also a literal object, the `println` reserved function will work and print the object name. In fact, a literal object provides the `to-string` method that returns the string representation of a literal object.

Symbol and argument access

Each nameset maintains a table of symbols. A symbol is a binding between a name and an object. Eventually, the symbol carries the `const` flag. During the lexical evaluation process, the lexical object tries to find an object in the nameset hierarchy. Such object can be either a symbol or an argument. Again, this process is transparent, but can be controlled manually. Both lexical and qualified named object have the `map` method that returns the first object associated in the nameset hierarchy.

A symbol is also a literal object, so the `to-string` and `to-literal` methods will return the symbol name. Symbol methods are provided to access or modify the symbol values. It is also possible to change the `const` symbol flag with the `set-const` method.

A symbol name cannot be modified, since the name must be synchronized with the nameset association. On the other hand, a symbol can be explicitly constructed. As any object, the `=` operator can be used to assign a symbol value. The operator will behaves like the `set-object` method.

Closure

As an object, the `Closure` can be manipulated outside the traditional declarative way. A closure is a special object that holds an argument list, a set of closed variables and a form to execute. The mechanic of a closure evaluation has been described earlier. What we are interested here is the ability to manipulate a closure as an object and eventually modify it. Note that by default a closure is constructed as a lambda expression. With a boolean argument set to true the same result is obtained. With false, a gamma expression is created.

This example creates an empty closure. The default closure is equivalent to the `trans f nil nil`. The same can be obtained with `const f (Closure true)`. For a gamma expression, the following forms are equivalent, `const f (Closure false)` and `const f nil nil`. Remember that it is `trans` and `const` that differentiate between a lambda and a gamma expression. Once the closure object is defined, the `set-form` method can be used to bind a form.

There are numerous situations where it is desirable to mute dynamically a closure expression. The simplest one is the closure that mute itself based on some context. With the use of `self`, a new form can be set to the one that is executed. Another use is a mechanism call advice, where some new computation are inserted prior the closure execution. Note that appending to a closure can lead to some strange results if the existing closure expression uses return special forms. In a multi-threaded environment, the ability to change a closure expression is particularly handy. For example a special thread could be used to monitor some context. When a particular situation develops, that threads might trigger some closure expression changes. Note that changing a closure expression does not affect the one that is executed. If such change occurs during a recursive call, that change is seen only at the next call.

Librarian and resolver

This chapter covers the use of the `axl` librarian utility as well as the `Librarian` object. The file path resolver is also described as a mean to search for a particular file to execute in a program.

Librarian object

A librarian file is a special file that acts as a containers for various files. A librarian file is created with the `axl` -- AFNIX cross librarian -- utility. Once a librarian file is created, it can be added to the interpreter resolver. The file access is later performed automatically by name with the standard interpreter `load` method.

Creating a librarian

The `axl` utility is the preferred way to create a librarian. Given a set of files, `axl` combines them into a single one.

The `-c` option creates a new librarian. The librarian file name is specified with the `-f` option.

The previous command combines `file-1.als` and `file-2.als` into a single file called `librarian.axl` . Note that any file can be included in a librarian.

Using the librarian

Once a librarian is created, the interpreter `-i` option can be used to specify it. The `-i` option accepts either a directory name or a librarian file. Once the librarian has been opened, the interpreter `load` method can be used as usual.

The librarian acts like a file archive. The interpreter file resolver takes care to extract the file from the librarian when the `load` method is invoked.

Librarian contents

The `axl` utility provides the `-t` and `-s` options to look at the librarian contents. The `-s` option returns all file name in the librarian. The `-t` option returns a one line description for each file in the librarian.

The one line report contains the file flags, the file size and the file name. The file flags are not used at this time. One possible use in the future is for example, an auto-load bit or any other useful things.

Librarian extraction

The `-x` option permits to extract file from the librarian. Without any file argument, all files are extracted. With some file arguments, only those specified files are extracted.

Librarian object

The `Librarian` object can be used within an AFNIX program as a convenient way to create a collection of files or to extract some of them.

Output librarian

The `Librarian` object is a standard AFNIX object. Its predicate is `librarian-p`. Without argument, a librarian is created in output mode. With a string argument, the librarian is opened in input mode, with the file name argument. The output mode is used to create a new librarian by adding file into it. The input mode is created to read file from the librarian.

The `add` method adds a new file into the librarian. The `write` method the full librarian as a single file those name is `write` method argument.

Input librarian

With an argument, the librarian object is created in input mode. Once created, file can be read or extracted. The `length` method -- which also work with an output librarian -- returns the number of files in the librarian. The `exists-p` predicate returns true if the file name argument exists in the librarian. The `get-names` method returns a vector of file names in this librarian. The `extract` method returns an input stream object for the specific file name.

Most of the time, the librarian object is used to extract file dynamically. Because a librarian is mapped into the memory at the right offset, there is no worry to use big librarian, even for a small file. Note that any type of file can be used, text or binaries.

File resolver

The AFNIXfile resolver is a special object used by the interpreter to resolve file path based on the search path. The resolver uses a mixed list of directories and librarian files in its search path. When a file path needs to be resolved, the search path is scanned until a matched is found. Because the librarian resolution is integrated inside the resolver, there is no need to worry about file extraction. That process is done automatically. The resolver can also be used inside an AFNIX program to perform any kind of file path resolution.

Resolver object

The resolver object is created without argument. The `add` method adds a directory path or a librarian file to the resolver. The `valid` method checks for the existence of a file. The `lookup` method returns an input stream object associated with the object.