We have already seen arithmetic expressions and string concatenation expressions. Such an expression may be assigned to a variable in an assignment statement, or passed as an argument in an object creation statement, or passed as an arguemnt to a message in a message send statement.
If we have an object descriptor with signature
name(arg1,...,argn)~
then name(
followed by n
expressions followed
by )
is an expression with the value of the object
created by this call. Note name(
should be regarded as
an indivisible symbol, as name
with some white space followed
by (
means something else which will be covered later.
Aldwych is limited in its type checking, but it will flag an error if
an expression which evaluates to an object is put in the place for
a non-object argument and vice-versa.
If P
is an object variable, then
P.mess(arg1,...,argm)
is an expression which evaluates
to the return value of sending message mess(arg1,...,argm)
to P
, that is, it is equivalent to using a variable
v
in an expression and having a separate statement
P.mess(arg1,...,argm)->v
. In this case there is no
type-checking, so it is up to the programmer to ensure that P
can take a message of that sort which returns a value or an object as
the expression in used. A message send expression can have multiple
messages, but return variables must be given for all but the last for
every message that requires a return variable. For example, the expression
P.mess1(x).mess2(y)->z.mess3(u)
is equivalent to
v
with separate statement
P.mess1(x).mess2(y)->z.mess3(u)->v
If an expression evaluates to an object, then that expression may be
used instead of an object variable name to send messages. For example,
given the time
objects of part 2,
time(5,50).add(20).clone
has the value of an object
representing the time 6:10. The object representing the time 5:50 is
constructed and sent the message add(20)
followed by
clone
without it being given a name, so we refer to it as
an anonymous object. Using this, the
following:
#main ==
aio\stdio().fwrite(Time.toString).nl,
time(5,50).add(20).clone->Time;
will cause 6:10
to be printed. The stdio
object is anonymous here, but the object returned from the clone
message is named Time
. That object could be anonymous as well,
giving:
#main == aio\stdio().fwrite(time(5,50).add(20).clone.toString).nl;
The code could also be written as:
#main ==
aio\stdio().fwrite(Time.toString).nl,
Time<-time(5,50).add(20).clone;
since here the value of the expression time(5,50).add(20).clone
is assigned to Time
.
As a.b.c
represents the return value of the c
message sent to a
after b
is sent to it,
if we want to send c
to the return value of b
sent to a
we use (a.b).c
. For example,
#main == aio\stdio().fwrite(t>" ">Time.toString).nl, Time<-time(5,50), t<-(Time.clone).add(30).toString;will cause
6:20 5:50
to be printed. A clone of the object
referred to be Time
is created, its time is advanced by
30 minutes and the result of sending the toString
message
to it after that is put in t
. Here
t<-Time.clone.add(30).toString
would not make sense, as it send a clone
message without
a return to Time
, but Time
has no rule to
handle such a message. Note also in this example, the expression
Time.toString
is used to mean the return value from sending
the toString
message to Time
, and this expression
forms part of a larger expression formed using the string concatenation
operator.
An alternative way of writing t<-(Time.clone).add(30).toString
is t<-Time~clone.add(30).toString
. The construct
a~b
sends
b
to a
but subsequent messages in the
expression are sent to the object which is returned from the b
message. This avoids complex use of brackets, for example,
a~b~c.d
is equivalent to ((a.b).c).d
.
~
they have <==>
which is followed by an expression formed from the arguments to the
procedure, and then a semicolon. For example, the following is a procedure
which gives the square of a number:
#square(m) <==> m*m;A
main
procedure which tests this is:
#main == aio\stdio() .fwrite("Enter an integer: ") .readInt->n .fwrite("Its square is: ">+square(n)) .nl;A procedure call, like
square(n)
above, evaluates to
the expression in the procedure with the parameters replaced by the
arguments of the call. The expression square(n)
may be
used in an assignment statement, for example x<-square(n)
,
or as part of a more complex expression in an assignment, for example,
x<-square(n)+2
. But a call to a procedure with a return
variable may be a statement on its own, such as square(n)->x
.
This is for completeness, analogous to x<-b.mess
and
b.mess->x
. It does not have a different meaning than
x<-square(n)
.
Procedures may have objects amongst their arguments. For example,
using time
objects, and including a main
to demonstrate it:
#halfHourLater(T) <==> T~clone.add(30).toString; #main == aio\stdio() .fwrite("Enter hours: ").readInt->hrs .fwrite("Enter minutes: ").readInt->mins .fwrite("Half an hour later is: ">halfHourLater(Time)) .nl, Time<-time(hrs,mins);Here, a
halfHourLater
call takes a time
as its argument and returns the string representing the time half
an hour later.
Procedures can return objects rather than values. In this case,
their headings have a different pattern. Here is a version of
halfHourLater
which returns the object representing the
time half an hour later than its argument (together with a
main
to demonstrate it):
#halfHourLater(T)~ ==<= T~clone.add(30).clone; #main == aio\stdio() .fwrite("Enter hours: ").readInt->hrs .fwrite("Enter minutes: ").readInt->mins .fwrite("Half an hour later is: ">Later.toString) .nl, Time<-time(hrs,mins), Later<-halfHourLater(Time);In this version of
halfHourLater
the first clone
message ensures that the object passed as a parameter is not changed by
the add(30)
message, while the second clone
message ensures a return is made from the procedure only after the
add(30)
message has been handled. The following would be
legal Aldwych:
#halfHourLater(T)~ ==<= L, T~clone->L, L.add(30);but due to the concurrency there is no guarantee here that the
toString
message will be sent to Later
after the add(30)
message inside halfHourLater
.
This version of halfHourLater
shows how a local variable
can be used inside a rule, in this case, L
. A local variable
has scope only within the rule and must have exactly one writer, and be
used at least once.