Here is an Aldwych single-rule procedure for calculating the factorial of an integer:
#fact(n) <== ?a\eq(n,0) :>1; :>n*fact(n-1);The expression
fact(n)
here has the value of the factorial
of n
. In part 3, we suggested the
following procedure for square
:
#square(m) <==> m*m;In fact the
<==>
here breaks into three symbols.
The first is <
which is attached to a procedure heading
following the closing bracket, and indicates the procedure returns a
non-object value. The alternative is ~
for a procedure
which returns an object, and that includes the object descriptors of
part 2. It is also possible to have procedures which
do not have any output, which we shall see later. The ==
symbol indicates a single-rule procedure. This means that a call to that
procedure rewrites straight away to the body with the appropriate
substitution of arguments for parameter variables; there is no waiting
for arguments to be bound before the rewriting may take place.
The final >
in <==>
indicates a return
statement. >exp
where exp
is any
expression returns the expression
as the return value of a single-rule procedure, or the return value of
a message in an object descriptor. If a procedure returns an object,
<=exp
is used as the return statement instead.
Following the single-assignment principle, there may be only one value
return or only one object return in a simple rule rhs (later we shall
see cases where there is both a value return and an object return).
The factorial procedure above shows a compound rhs. A rhs
may consist of a number of statements (in this factorial example the number
is zero) followed by a selection statement which takes the form
?<boolean expression>:<rhs>:<rhs>
.
If the rule requires a return and none of the statements before the
selection statement is one, then both the subsidiary rhss in the
selection statement must have one. The subsidiary rhss may themselves
contains selection statements, enabling more complex selection statements
to be constructed.
Aldwych does not use infix boolean operators in rhss, hence the boolean
expression above takes the form a\eq(n,0)
. This uses the
procedure eq
from Aldwych's built-in library a
.
Unlike aio
the library a
is not mentioned separately
when the KLIC file is compiled. eq
is an equality
test on integers and strings. a
also contains inequality
tests ne
, gt
, ge
, lt
,
and le
, meaning "not equal", "greater than",
"greater than or equal", "less than" and "less than or equal"
respectively. With strings the ordering used is alphabetically.
Compound boolean expressions may be built up using a ,
symbol to mean "and" and a ;
symbol to mean "or". These
symbols have these meanings only in the context of a boolean expression
in a selection statement, and the usual rule that :and" has higher
precedence than "or" is kept.
Using selection statements, we can now write a version of time
which gives the correct string when the number of minutes past the hour is
less than ten:
#time(-hours,-mins)~ <(totmins<-hours*60+mins) { clone~ |>time(0,totmins); hours- |>totmins/60; minutes-|>totmins//60; add(n) | totmins+=n; sub(n) | totmins-=n; toString-| ?a\eq(totmins//60,0) :>"">+totmins/60>":00"; :?a\lt(totmins//60,10) :>"">+totmins/60>":0">+totmins//60; :>"">+totmins/60>":">+totmins//60 }This is not very satisfactory, however, due to the need to recalculate the hours figure up to three times. A better form of the
toString
rule calculates the hours and minutes
values first and passes them in as local variables to the selection
statement:
toString-| <(hrs<-totmins/60,mins<-totmins//60) ?a\eq(mins,0) :>"">+hrs>":00"; :?a\lt(mins,10) :>"">+hrs>":0">+mins; :>"">+hrs>":">+minsThe layout has also been tidied up to indicate an "else if" structure.
Although local variables may be freely shared in the statements on a rhs,
if they are to be used in the selection statement which ends it, they have
to be passed in, in a list starting with <(
, ending with
)
and separated by commas. In the above, the local variables
are given values and passed at the same time. An alternative where the
values are given separately is:
toString-| hrs<-totmins/60,mins<-totmins//60, <(hrs,mins) ?a\eq(mins,0) :>"">+hrs>":00"; :?a\lt(mins,10) :>"">+hrs>":0">+mins; :>"">+hrs>":">+mins
#fact(n)< { n=0 ||>1; n>0 ||>n*fact(n-1); }Note the lhs and rhs of the rules are separated by double bars rather than single bars as in object descriptors as described in part 2. The lhs consists of conditions on the arguments to the procedure, which do use infix operators. The infix operators which may be used are
>
, >=
, <
,
<=
, =
, ==
, #
and
\=
. The symbol =
is used with its left hand argument
a variable and its right-hand argument a number or a string, whereas
==
is used to compare two variables for equality. A
similar distinction is made for inequality between #
and
\=
, with the latter the one to compare two variables.
As a procedure call returns a value, each rule in it
return a value, using the >
form previously described
for returning values for object messages. For procedures that return objects,
the <=
return form is used.
A procedure call will evaluate using one of its rules, once it has done so there is no returning and trying other rules (this is mentioned because the logic programming language Prolog has as a major feature such "backtracking"). Since computation is concurrent in Aldwych, a procedure call may be set up before all its arguments variables have been written to. A comparison operator requires both arguments to be written to, and will suspend evaluation until both have been written to. A procedure call will evaluate using one of its rules if all the variables that rule's lhs requires are written to and its tests evaluate to true, even if other rules are suspended.
A rule statement may occur at the end of a rule rhs. In an
object descriptor, it works like a call to a procedure which takes
the arguments of that object, plus any arguments in the message on
the lhs of the rule, plus any additional local arguments passed in
as with selection statements. It returns a reply for the message.
For example, the rule for toString
in time
could be written:
toString-| <(hrs<-totmins/60,mins<-totmins//60) { mins=0 ||>"">+hrs>":00"; mins>0,mins<10 ||>"">+hrs>":0">+mins; mins>=10 ||>"">+hrs>":">+mins; }Here, the second rule in the inner rule set shows that more than one comparison may occur on the lhs of a rule. All comparisons on the lhs must succeed for the rule to be applicable, so the commas between them could be considered as meaning "and". However, unlike selection statements there is no "or". Also note that whereas conditional expressions in selection statements contain procedure calls and not direct comparisons, in rule statements it is the other way round - procedure calls may not be used on the lhs of a rule.
A :
symbol immediately after a rule in a set of selection rules
means "otherwise". This can be used to simplify rules. Rules following
an otherwise are tried only after the lhss all those of all those before it
have failed. Even if a rule before an otherwise is suspended, rules after
it will not be attempted until the suspension is ended and the lhs
failed. More than one otherwise may be used in a set of rules. So
the factorial procedure could be written:
#fact(n)< { n=0 ||>1; : ||>n*fact(n-1); }and the rule for the
toString
method of time
could be written:
toString-| <(hrs<-totmins/60,mins<-totmins//60) { mins=0 ||>"">+hrs>":00"; : mins<10 ||>"">+hrs>":0">+mins; : ||>"">+hrs>":">+mins; }The blank lhs of the final inner rule here means it always applies if rule consideration reaches this point after the previous otherwises.
Apart from "otherwise", no ordering is assumed on rules. If more than one rule is applicable, any of the applicable rules could be chosen for use, which one is indeterminate.