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:
proc name arguments : result type{ body }for example:
proc readName () : string { print ('Name?'); return read (); }drawn as:
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:
proc hello (name:string) { print ('Hello, ' ^ name ^ '.'); }is drawn:
proc readName () : string { print ('Name?'); return read (); }
hello (readName ())
After the functions have been expanded, we are left with the graph:
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); }drawn:
double (2)
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;Recursive functions are drawn as cyclic graphs:
proc hello () {
print ('Name?');
print ('Hello, ' ^ read () ^ '.');
hello ()
}
Note that recursive declarations can only be made of proc function declarations.
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) {This is drawn:
proc fog (x:int) { return f(g(x)); }
return fog;
}
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) {and drawn:
return fn proc (x:int) { return f(g(x)); };
}
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));
}
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:
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:
if (x) { print ('hi') } { print ('lo') }drawn: