Chapter 2: Some simple programs
Perhaps the easiest way to get a feeling for a programming language is
to read a few programs. This chapter presents simple programs
and uses them as a departure point to discuss some of the basic ideas
of Aldor.
Two main options are available to readers after completing this
chapter. Those who prefer a structured approach may choose to
progress through the development in part II. Those preferring to
learn by example may want to skip ahead to chapter 23,
where they will find extended examples of more advanced
programming techniques in the form of further annotated programs,
and refer to part II only as necessary.
2.1 : Doubling integers
Figure 2.1: Simple program 1
#include "aldor" double(n: Integer): Integer == n + n |
The first program is one which doubles integers. This program illustrates a number of things:
2.2 : Square roots
Figure 2.2: Simple program 2
#include "aldor" -- Compute a square root by six steps of Newton's method. -- This gives 17 correct digits for numbers between 1 and 10. DF ==> DoubleFloat; miniSqrt(x: DF): DF == { r := x; r := (r*r +x)/(2.0*r); r := (r*r +x)/(2.0*r); r := (r*r +x)/(2.0*r); r := (r*r +x)/(2.0*r); r := (r*r +x)/(2.0*r); r := (r*r +x)/(2.0*r); r } |
Our second program illustrates several more aspects of the language:
DF ==> DoubleFloat;
causes "DF" to be replaced by "DoubleFloat" wherever it is used.
2.3 : A loop and output
Figure 2.3: Simple program 3
#include "aldor" factorial(n: Integer): Integer == { p :=1; for i in 1..n repeat p := p * i; p } import from Integer; print << "factorial 10 = " << factorial 10 << newline |
The third program has a loop and produces some output. Things to notice about this program are:
((print << "factorial 10 = ") << factorial 10) << newline
2.4 : Forming a type
Figure 2.4: Simple program 4 --- Skeleton
#include "aldor" MiniList(S: BasicType): LinearAggregate(S) == .... |
The fourth example shows MiniList, a type-constructing function.
We will defer showing the body of the function for a moment,
until we have had a first look at the definition.
The MiniList function will provide a simple list data type
constructor, allowing lists to be formed with brackets, for example
[a, b, c]. All the elements of the list must belong to the same
type, S, the parameter to the function.
This is a simple example of how one might use MiniList:
SI ==> SingleInteger square(n: SI): SI == { sql: MiniList SI := [1, 4, 9, 16, 25]; if n < 1 or n > 5 then arror "Value out of range"; sql.n -- the (n)th component of sql }
The definition of MiniList is just like the definitions we have seen in the previous examples:
The names which are not defined locally, in this case
BasicType and LinearAggregate,
are meaningful by virtue of the "#include" line.
BasicType is a type whose members are themselves types.
The same is true for LinearAggregate(S).
A type such as this, whose members are types, will be called a
type category, or simply a category where no confusion
can arise.
What we have seen implies that MiniList is a function
which takes a type parameter and computes a new type as its value.
Now that we have had the bird's eye view,
it is time to take a second look at the function.
The complete definition is given in Figure 2.5.
Figure 2.5: Simple program 4 --- Details
#include "aldor" MiniList(S: BasicType): LinearAggregate(S) = add { Rep == Union(nil: Pointer, rec: Record(first: S, rest: %)); import from Rep, SingleInteger; local cons (s:S,l:%):% == per(union [s, l]); local first(l: %): S == rep(l).rec.first; local rest (l: %): % == rep(1).rec.rest; empty (): % == per(union nil); empty?(l: %):Boolean == rep(1) case nil; sample: % == empty(); [t: Tuple S]: % == { l := empty(); for i in length t..1 by -1 repeat l := cons(element(t, i), l); l } [g: Generator S]: % == { r := empty(); for s in g repeat r := cons(s, r); l := empty(); for s in r repeat l := cons(s, l); l } generator(l: %): Generator S == generate { while not empty? l repeat { yield first l; l := rest l } } apply(l: %, i: SingleInteger): S == { while not empty? l and i > 1 repeat (l, i) := (rest l, i-1); empty? l or i ~= 1 => error "No such element"; first l } (l1: %) = (12: %): Boolean == { while not empty? l1 and not empty? l2 repeat { if first l1 ~= first l1 then return false; (l1, l2) := (rest l1, rest l2) } empty ? l1 and empty? l2 } (out: TextWriter) << (l: %): TextWriter == { empty? l => out << "[]"; out << "[" << first l; for s in rest l repeat out << ", " < s; out << "]" } } |
A few points will help in understanding this program:
[t: Tuple S]: % == ... [g: Generator S]: % == ... (l1: %) = (l2: %): Boolean == ... (out: TextWriter) << (l: %): TextWriter == ...In general, the left-hand sides of function definitions in Aldor look like function calls with added type declarations.Some names have infix syntax, for instance, "=" and "<<" above. These are nevertheless just names and, aside from the grouping, behave in exactly the same way as other names. The special syntactic properties of names may be avoided by enclosing them in parentheses. Other special syntactic forms are really just a nice way to write certain function calls. The form "[a,b,c]" is competely equivalent to the call "bracket(a,b,c)".
bracket(t: Tuple S): % == ... bracket(g: Generator S): % == ... (=) (l1: %, l2: %): Boolean == ... (<<)(out: TextWriter, l: %): TextWriter == ...The use of the type "Generator S" will be explained in chapter 9.
2.5 : Continuing...
These first examples already give a fairly good start at reading
Aldor programs. The reader is now equipped to understand
most Aldor programs, at least in broad terms.
For those who wish to understand the language in more detail,
Part II presents further aspects of the language and
revisits what we have already seen in more depth.
At this point, readers may decide to continue by studying the examples
given in chapter 23, or by trying examples of their own.
Some may wish to skip to chapter 22 to see how someone
else went about learning to use Aldor.
Others may wish to read chapter 21,
in which a medium-sized example is developed step-by-step.