Chapter 11: Generic tie-ins
11.1 Literals
11.2 Program-defined tests
11.3 Generator
11.4 Apply
11.5 Set!
11.6 Bracket
11.7 Coerce
Several syntactic constructs are defined in terms of function application in Aldor:
This allows user programs to define how these syntactic constructs
behave in a particular lexical context. The function calls themselves
are treated as standard function calls, so the normal rules apply.
11.1 : Literals
Types provide meanings for literal constants by defining
functions named "integer
", "float
" or "string
"
taking values of type Literal
. The type Literal
represents the source text of the particular literal. Valid literal
values are described in section 5.2.
The meaning of string-style literals is determined by what operations
string: Literal -> X
are visible, where X may be any type.
Both in #include "aldor"
and #include "axiom"
the type String
provides string-style literals.
Both integer and floating point literals are passed in the same way,
so it is legal to call the function string on them.
This can be used to allow numbers to be parsed as
strings. For example, the function scanNumber
, from
NumberScanPackage in the Aldor base library, converts a string
into an element of an arithmetic type. The following example is from
the implementation of arbitrary precision floats in theAldor base
library.
float(l: Literal): % == { import from NumberScanPackage %; scanNumber string l }
Note that the literal "l
" is converted to a string, and then
scanNumber is called on the result to form a new value.
11.2 : Program-defined tests
There are several types of expression in which a condition controls the evaluation of an Aldor program:
if condition then ... condition => ... while condition repeat ... for ... in ... | condition repeat ... not condition condition1 and condition2 condition1 or condition2
In many situations, a value can be treated as a condition, even though
it may not be a value from the "Boolean
" type. Aldor allows
these to be treated as logical values in the above constructs. If a
condition above produces a value of type T, different from
Boolean, and there is a single function "test: T ->
Boolean
" in scope, then that
"test
" function is implicitly applied to the condition value to determine
the outcome of the test. For example:
#include "aldor" -- In "aldor" the type List(S) has a function "test" which -- returns true on non-empty lists. -- This function determines which of two lists is longer. LI ==> List Integer; longer(a: LI, b: LI): LI == { (a0, b0) := (a, b); while a and b repeat (a, b) := (rest a, rest b); if a then a0 else b0 }
11.3 : Generator
If an expression traversed by a "for
" iterator does not evaluate
to a Generator value, then the operator "generator
" is
implicitly applied to the expression. This makes loops more readable
if the for is traversing, for example, a list or an array:
#include "aldor" import from List Integer; ls := [1,2,3,4]; for elem in ls repeat print << elem << newline;
The "for elem in ls repeat ...
" of the previous example is
equivalent to:
for elem in generator ls repeat ...
When List Integer is imported, then the application:
generator: List(Integer) -> Generator(Integer)
comes into the current scope.
11.4 : Apply
In the absence of an explicit function named "a
", the application
"a(b)
" is treated as a call to the function "apply
" with the
first argument being taken to be "a
", and the remaining arguments
being taken from the arguments to the original application.
Example:
f(a,b,c) becomes apply(f,a,b,c)
For example, consider a matrix domain over a ring, R. A desirable syntax for retrieving elements of a particular matrix might be:
mat(a, b)
where a and b are integer indices for the matrix. To achieve this, a matrix domain would export a function with signature:
apply: (%, Integer, Integer) -> R;
The function is defined in the normal way.
11.5 : Set!
If the left hand side of an assignment is an application, the assignment is treated as an application of the operator set! to the operator of the left hand side, the operands of the left hand side and the right hand side.
f(a,b) := E becomes set!(f,a,b,E)
f a := E becomes set!(f,a,E)
f.a := E becomes set!(f,a,E)
f.a.b := E becomes set!(f.a,b,E)
As an example, consider the matrix domain above. We would like to assign into the matrix using a syntax like:
mat(a, b) := 1;
To achieve this, the domain should export a function with signature:
set!: (%, Integer, Integer, R) -> R ++ update and return the previous value of the element
As this is just a normal function, there are no restrictions on the
return type or value. In this case a value from R is returned, and
the description indicates which. Note that the description is purely
for documentation purposes, and not used to interpret the program.
11.6 : Bracket
An expression of the form:
[ Expr ]
is treated as an application of the operator "bracket
" to Expr.
Example:
#include "aldor" import from List Integer; a := [1,2,3]; b := bracket(1,2,3); print << a << newline; -- Produces: list(1,2,3) print << b << newline; -- Produces: list(1,2,3)
As can be seen above, this is a useful syntax for creating aggregates
of various types. The value passed to the "bracket
" function in
this case is a tuple of the three values 1, 2 and 3.
11.7 : Coerce
An expression of the form:
Expr :: T
is treated as an application of the operator coerce
to
Expr and restricted to be of type T:
coerce( Expr )@T
This allows types to define their own mechanisms for converting
between types, and enables types to do appropriate error checking.