<- ^ ->
Functional values

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. This is called higher order 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;
        }
        
        *(void (<'a>list)) mapper(*(void ('a)) 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. *(void ('a)) is function that takes single argument, of any type, and returns no value. Similarly *(string (int, <'a>list)) is function, that takes int and <'a>list parameters, and returns string.

8.2   That's weird, I can't get it!

First step is to use higher order functions, next is to write them. Gont standard library includes several modules involving higher order functions. One such example is List::iter() function. It is defined as:

        void List::iter(*(void ('a)) f, <'a>List::t lst);
Let's say you have list of integers and want to print it out:

        <int>List::t some_list;
        // ... here it is assigned some value ...

        void print_element(int el)
        {
                print_int(el);
                print_newline();
        }

        List::iter(print_element, some_list);
List::iter() applies given function to each element of list.

8.3   More examples

        // Call passed function f on each element of the list l
        // starting from head.
        void iter(*(void ('a)) 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(*('b ('a)) 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.4   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:

        *(void (<'a>list)) mapper(*(void (int)) f) 
        {
                *(void (<'a>list)) for_all;
                
                for_all = fun void (<'a>list lst) {
                        while (lst != null) {
                                f(lst.data);
                                lst = lst.next;
                        }
                };

                return for_all;
        }
Or even:

        *(void (<'a>list)) mapper(*(void (int)) f) 
        {
                return fun void (<'a>list lst) {
                        while (lst != null) {
                                f(lst.data);
                                lst = lst.next;
                        }
                };
        }
fun is keyword. After fun comes return type, then list of parameters, along with their types, just like in regular function definition in C. Then comes body of function -- either block or expression in parenthesis.

8.5   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 (int a, int b) (a + b)
is equivalent of:

        fun int (int a, int b) { return a + b; }

8.6   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.

<- ^ ->
Functional values