Functions

Function declarations

Functions can be declared:

proc name arguments { body }
for example:
proc hello (name:string) { print ('Hello, ' ^ name ^ '.'); }
Function declarations are drawn by putting a box round the function body, for example:
If the function returns a result it can be declared with:
proc name arguments : result type{ body }
for example:
proc readName () : string { print ('Name?'); return read (); }
drawn as:
Functions can be applied in the usual manner:
hello ('Fred')
drawn with an application node decorated with `@' with a number of incoming data arcs: one for the function, and the rest for the arguments:
For example:
proc hello (name:string) { print ('Hello, ' ^ name ^ '.'); }
proc readName () : string { print ('Name?'); return read (); }
hello (readName ())
is drawn:

After the functions have been expanded, we are left with the graph:

Categories of functions

All of the above functions have been declared with the keyword `proc', indicating that they are processes. However, we can also declare value or central functions. Value functions can only contain value nodes, and central functions can only contain value and central nodes.

For example we could declare a function to double an integer as:

proc double (n:int) : int { return (n+n); }
double (2)
drawn:
but as can be seen from the resulting flow graph, the function has no control dependencies. We can record this by declaring the function as a val, rather than as a proc:
Note that the application node is now blue, recording the fact that this is a value function, not a process function. These blue application nodes are value nodes. Similarly, we can declare central functions, with central application nodes:

Recursive functions

By default, functions are not recursive, but recursive functions can be declared using the keyword `rec'. For example, a recursive `hello user' program is:

rec hello;
proc hello () {
   print ('Name?');
   print ('Hello, ' ^ read () ^ '.');
   hello ()
}
Recursive functions are drawn as cyclic graphs:

Note that recursive declarations can only be made of proc function declarations.

Higher-order functions

In common with langauges such as SML, Premon allows functions to be treated as data, and passed as arguments or returned as results. For example, a function which composes two integer functions together can be declared:

val compose (proc f(int):int, proc g(int):int) (proc(int):int) {
   proc fog (x:int) { return f(g(x)); }
   return fog;
}
This is drawn:
Note that compose is a val function, even though its arguments and result are proc functions. This is because function declarations are always values, even if the function body is a process or is central.

Anonymous functions

In common with languages such as SML, functions do not have to be named. Instead we can use anonymous functions which are expressions of the form:

fn proc arguments { body }
and similarly for `fn central' and `fn val'. For example the composition function could be written more succinctly as:
val compose (proc f(int):int, proc g(int):int) (proc(int):int) {
   return fn proc (x:int) { return f(g(x)); };
}
and drawn:

This is such a common usage that we provide syntax sugar for it, writing:

proc compose (proc f(int):int, proc g(int):int) (x:int) {
   return f(g(x));
}

Thunks

A thunk is a function with no arguments. Thunks are used to control the order of evaluation of a function. For example, an `if' function should not evaluate its arguments until we know whether the boolean is true or false. The obvious way to provide an `if' statement is as a constructor of type:

proc if (bool) ((),())
and then used for example:
if (x) (print ('hi'), print ('lo'))
drawn:
Unfortunately, as the flow graph makes obvious, this doesn't work, since `lo` cannot be printed until `hi` has been, even if the boolean is false.

Instead, we need to thunk the two arguments, to stop them evalutating until the boolean condition is known. Now if has the type:

proc if (bool) (proc()) (proc());
and then used for example:
if (x) (fn proc () { print ('hi') }) (fn proc () { print ('lo') })
drawn:
This works, but is rather clumsy, so we provide some syntax sugar for thunking, writing `{M}' for `fn proc () {M}', for example:
if (x) { print ('hi') } { print ('lo') }
drawn:
Previous | Next