prev up next

## 6) Functions

Aldwych allows a style of programming close to that of functional programming, with functions passed as arguments, and curried functions (functions which take only some of the arguments of a "full" function call and return a function which can take the rest).

A procedure which is intended to be used in a functional style is written as the procedures we have seen previously, but its argument list is enclosed in square brackets and the arguments are separated by white space, not commas. For example, here is the procedure we saw previously to square a number:

```#square(n) <==> n*n;
```
but here is the functional version:
```#square[n] <==> n*n;
```
A function is set up from its name by following it with `()`. So to get the square of a number stored in variable `m`, we use `square() m`. Alternatively, a variable could be set to refer to the function: `Square<-square()` followed by `Square m`. Note that Aldwych treats the function as an object.

Using this, the following Aldwych program reads in a sequence of numbers, stores them in a list, maps the `square` function to the list, and prints out the result:

```#main ==
Screen<-aio\stdio(),
writeNumsList(Screen,map(square(),list)),
Screen.fwrite("Enter some numbers terminated by zero: "),

{
n=0 ||>\$;
}

#writeNumsList(Screen,list)=
{
list\$  ||
Screen.nl;
writeNumsList(Screen,tail)
}

#map(Func,list)<
{
list\$ ||>\$;
}

#square[n] <==> n*n;
```
The procedure `map` takes a function and a list and returns the list that consists of the result of applying the function to each element of the original list. Of course, `map` could be written in a functional style as well, making the program (omitting the reading and writing procedures, which are unchanged):
```#main ==
aio\stdio()->Screen,
writeNumsList(Screen,map() square() list),
Screen.fwrite("Enter some numbers terminated by zero: "),

#map[Func list]<
{
list\$ ||>\$;
}

#square[n] <==> n*n;
```
In Aldwych, juxtaposition (with white space separating symbols) means function application. It is left-associative (as in standard functional programming), so `A B C` is interpreted as `(A B) C` rather than `A (B C)`, and has higher precedence than the cons operator `:` or the arithmetic operators, but not higher than the message send operators.

Precedence of message send against function application is demonstrated in the example below:

```#main ==
Screen<-aio\stdio(),
writeNumsList(Screen,map() square() List),
Screen.fwrite("Enter some numbers terminated by zero: "),

#map[Func ListObj] <==
?ListObj.more
:>Func ListObj.next : map() Func ListObj;
:>\$;

#square[n] <==> n*n;

#listobj(list)~
{
more-[
list\$ |>#;
:   |>=
]
}
```
This involves an object with a mutable state, as described in part 2. Here the object type is `listobj`. A `listobj` object has a list as its internal state, and takes two messages, `next` which returns the head of the list and sets the state variable to the tail of the list, and `more` which returns a boolean value saying whether the list is empty or not. It can be considered a sort of reader in which `more` says whether there are more items to read, and `next` gives the next item.

In `map` here, `Func ListObj.next : map() Func ListObj` is parsed as `(Func (ListObj.next)) : ((map() Func) ListObj)` due to the precedence and associativity of the operations. The code for `listobj` shows the Aldwych symbols for boolean literals, `#` for "false" and `=` for "true".

This example shows how in Aldwych it is possible to mix a functional style with mutable objects. A `listobj` object is mutable. Whenever a `next` message is sent to it, it changes its state, so that the next `next` message returns the next item in the list. For example, the following code:

```#main ==
Screen<-aio\stdio(),
list1 <- map() square() List,
list2 <- map() (add() 100) List,
writeNumsList(Screen,list1),
writeNumsList(Screen,list2),
Screen.fwrite("Enter some numbers terminated by zero: "),

#map[Func ListObj] <==
?ListObj.more
:>Func ListObj.next : map() Func ListObj;
:>\$;

#square[n] <==> n*n;

#listobj(list)~
{
more-[
list\$ |>#;
:   |>=
]
next-[
:              |>0
]
}
```
will not create two lists, one from mapping `square` onto the original sequence of numbers, the other from mapping `add() 100` to it (i.e., the function which takes a number and returns the number 100 greater). This is because when `map` from either mapping sends a `next` message to the shared `listobj` object, it causes the state of that object to change. The state change is global for all computations sharing the `listobj` object.

It is also necessary here to alter the code for `listobj` so that it is able to handle `next` messages when the internal state holds the empty list. Here it returns `0`. In the first example of `listobj` the rule for `next` also had the condition that the internal `list` variable store a value with the pattern `head:tail`. Aldwych does not have a way of dealing with messages where none of the rules matches. Although the code for `map` would seem to guard against receiving a `next` message when the internal state holds an empty list, it is possible. This is because a `next` message from a concurrent computation can come between the `more` message sent by `map` and the `next` message sent if the `more` message returns "true".

Note that it is possible to mix the functional and procedural style. For example, the `add` function could have been written:

```#add(m)[n] <==> m+n;
```
This results in an add function that can be curried to abstract out the first argument, but not the second. The line calling `add` above would have to be replaced by `list2 <- map() add(100) List`.

Here is a version of `map` defined in the way suggested by John Hughes in his well-known paper Why Functional Programming Matters:

```#reduce[F x l]<
{
l?y |>F y <;
: ||>x;
}

#dot[F G x] <==> F (G x);

#cons[h t]<==> h:t;

#map[F]~ ==<= reduce() (dot() cons() F) [];
```