Next: Calling higher-order terms, Up: Higher-order [Contents]

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: Calling higher-order terms, Up: Higher-order [Contents]