A Tour of Morfa

Function templates

Recall the template List from the section on class and struct templates.

A generic function that operates on lists of elements of any type, for example counts the number of elements in a list, may be defined using a function template:

import advanced.templates.class_templates;

// Warning: does not terminate for cyclic lists!
public template <T>
func length(list: List<T>): int
{
    return if (list == null) 0
           else 1 + length(list.tail);
}

unittest
{
    var list = new Ints(1, new Ints(2, new Ints(3, null)));
    assert (length<int>(list) == 3);
}

Here, the expression length<int> refers to an instance of the template length for the parameter T equal to int, just as List<int> refers to an instance of Length for T equal to int.

Template argument deduction for function templates

When calling a function template you may usually omit the template arguments (here <int>) and let the compiler infer them for you by looking at the call arguments. In the case of the above call to length, since the type of list is List<int> the call makes sense only length is instantiated for int as well.

unittest 
{
    var list = new Ints(1, new Ints(2, new Ints(3, null)));
    assert (length(list) == 3);

    // However, this wouldn't work without
    // explicit instantiation with <int>:
    // assert (length(null) == 0);
    assert (length<int>(null) == 0);

    // Instead you may do:
    var noInts: Ints = null;
    assert (length(noInts) == 0);
}

Function template argument deduction allows us to create lists using a more concise notation, while the compiler infers all the boring details:

public template <T>
func cons(head: T, tail: List<T>): List<T>
{
    return new List<T>(head, tail);
}

unittest
{
    var ints = cons(1, cons(2, cons(3, null)));
    assert (length(ints) == 3);
}

If you are in a mood for a more fancy notation:

public operator :: { kind = infix, precedence = mul, associativity = right }

public template <T>
func :: (head: T, tail: List<T>): List<T>
{
    return new List<T>(head, tail);
}

unittest
{
   var ints = 1 :: 2 :: 3 :: null;
}