Next: , Up: Higher-order   [Contents]


8.1 Creating higher-order terms

To create a higher-order predicate or function term, you can use a lambda expression, or, if the predicate or function has only one mode and it is not a zero-arity function, you can just use its name. For example, if you have declared a predicate

:- pred sum(list(int), int).
:- mode sum(in, out) is det.

the following unifications have the same effect:

X = (pred(List::in, Length::out) is det :- sum(List, Length))
Y = sum

In the above example, the type of ‘X’, and ‘Y’ is ‘pred(list(int), int)’, which means a predicate of two arguments of types ‘list(int)’ and ‘int’ respectively.

Similarly, given

:- func scalar_product(int, list(int)) = list(int).
:- mode scalar_product(in, in) = out is det.

the following three unifications have the same effect:

X = (func(Num, List) = NewList :- NewList = scalar_product(Num, List))
Y = (func(Num::in, List::in) = (NewList::out) is det
        :- NewList = scalar_product(Num, List))
Z = scalar_product

In the above example, the type of ‘X’, ‘Y’, and ‘Z’ is ‘func(int, list(int)) = list(int)’, which means a function of two arguments, whose types are ‘int’ and ‘list(int)’, with a return type of ‘int’. As with ‘:- func’ declarations, if the modes and determinism of the function are omitted in a higher-order function term, then the modes default to ‘in’ for the arguments, ‘out’ for the function result, and the determinism defaults to ‘det’.

The Melbourne Mercury implementation currently requires that you use an explicit lambda expression to specify which mode you want, if the predicate or function has more than one mode (but see below for an exception to this rule).

You can also create higher-order function terms of non-zero arity and higher-order predicate terms by “currying”, i.e. specifying the first few arguments to a predicate or function, but leaving the remaining arguments unspecified. For example, the unification

Sum123 = sum([1,2,3])

binds ‘Sum123’ to a higher-order predicate term of type ‘pred(int)’. Similarly, the unification

Double = scalar_product(2)

binds ‘Double’ to a higher-order function term of type ‘func(list(int)) = list(int)’.

As a special case, currying of a multi-moded predicate or function is allowed provided that the mode of the predicate or function can be determined from the insts of the higher-order curried arguments. For example, ‘P = list.foldl(io.write)’ is allowed because the inst of ‘io.write’ matches exactly one mode of ‘list.foldl’.

For higher-order predicate expressions that thread an accumulator pair, we have syntax that allows you to use DCG notation in the goal of the expression. For example,

Pred = (pred(Strings::in, Num::out, di, uo) is det -->
    io.write_string("The strings are: "),
    { list.length(Strings, Num) },
    io.write_strings(Strings),
    io.nl
)

is equivalent to

Pred = (pred(Strings::in, Num::out, IO0::di, IO::uo) is det :-
    io.write_string("The strings are: ", IO0, IO1),
    list.length(Strings, Num),
    io.write_strings(Strings, IO1, IO2),
    io.nl(IO2, IO)
)

Higher-order function terms of zero arity can only be created using an explicit lambda expression; you have to use e.g. ‘(func) = foo’ rather than plain ‘foo’, because the latter denotes the result of evaluating the function, rather than the function itself.

Note that when constructing a higher-order term, you cannot just use the name of a builtin language construct such as ‘=’, ‘\=’, ‘call’, or ‘apply’, and nor can such constructs be curried. Instead, you must either use an explicit lambda expression, or you must write a forwarding predicate or function. For example, instead of

list.filter(\=(2), [1, 2, 3], List)

you must write either

list.filter((pred(X::in) is semidet :- X \= 2), [1, 2, 3], List)

or

list.filter(not_equal(2), [1, 2, 3], List)

where you have defined ‘not_equal’ using

:- pred not_equal(T::in, T::in) is semidet.
not_equal(X, Y) :- X \= Y.

Another case when this arises is when want to curry a higher-order term. Suppose, for example, that you have a higher-order predicate term ‘OldPred’ of type ‘pred(int, char, float)’, and you want to construct a new higher-order predicate term ‘NewPred’ of type ‘pred(char, float)’ from ‘OldPred’ by supplying a value for just the first argument. The solution is the same: use an explicit lambda expression or a forwarding predicate. In either case, the body of the lambda expression or the forwarding predicate must contain a higher-order call with all the arguments supplied.


Next: , Up: Higher-order   [Contents]