8 Functional values
Functions are first class citizens. This functional programming slogan
means that you can write a function, that takes function as argument and
returns function. As this might sound a bit magic, let's make it look like
real voodoo: :)
8.1 Example
opt_struct <'a>list {
'a data;
<'a>list next;
}
*(<'a>list) -> void mapper(*('a) -> void f)
{
void for_all(<'a>list lst)
{
while (lst != null) {
f(lst.data);
lst = lst.next;
}
}
return for_all;
}
This function takes function f
operating on items, and returns function
that applies f
to all elements of list.
Ok, now some explanations. The key thing to note is functional type
notation. *('a) -> void
is function that takes single argument,
of any type, and returns no value. Similarly
*(int, <'a>list) -> string
is function, that takes int
and <'a>list
parameters, and returns string
.
8.2 More examples
// Call passed function f on each element of the list l
// starting from head.
void iter(*('a) -> void f, <'a>list l)
{
while (l != null) {
f(l);
l = l.next;
}
}
// Call function f on each element of the list l,
// collect results as a list (of possibly different type)
// and return it.
<'b>list map(*('a) -> 'b f, <'a>list l)
{
<'b>list o = null;
while (l != null) {
o = {data = f(l.data), next = o};
l = l.next;
}
return o;
}
Following functions are defined in Gont standard library:
string itoa(int);
void print_string(string);
We can use them with our iter
and map
:
<int>list il = ({ data = 1, next = ({ data = 2, next = null })});
<string>list sl = map(itoa, il);
iter(print_string, sl);
This will print ``12''.
8.3 MLish variations about defining functions
Nested function definitions (for example the for_all
function) are
shortcut to defining functional variables and initializing them, so our
example could look as:
*(<'a>list) -> void mapper(*(int) -> void f)
{
*(<'a>list) -> void for_all;
for_all = fun (<'a>list lst) -> void is {
while (lst != null) {
f(lst.data);
lst = lst.next;
}
};
return for_all;
}
Or even:
*(<'a>list) -> void mapper(*(int) -> void f)
{
return fun (<'a>list lst) -> void is {
while (lst != null) {
f(lst.data);
lst = lst.next;
}
};
}
fun and is are keywords. After fun comes
list of parameters, along with their types, just like in regular function
definition in C. Then comes ->, return type, is and
finally body of function.
8.4 Omitting return keyword
Special extension is supported, useful when dealing with functional
values. Whenever function body (sequence of statements within { }
)
should appear, single expression within ( )
can be supplied. It is
passed to return statement, which is only statement in function body. So:
fun (int a, int b) -> int is (a + b)
is equivalent of:
fun (int a, int b) -> int is { return a + b; }
8.5 Closures
This section is for curious folks, who always want to know, how things
work.
Local functions needs to remember variables from enclosing function, at
the time they were defined. For example for_all
function from
our example needs f
parameter of mapper
. However
for_all
cannot rely on being called when mapper
activation
record is still on the stack.
Therefore local function are stored as pair of pointer to function
and pointer to special closure structure. Closure of a function holds
all variables defined in it, that might need to be accessed by local
functions. Local functions, in generated code, are always passed
closure to enclosing function as first argument.