[m-users.] Returning a predicate from a function

Julien Fischer jfischer at opturion.com
Fri Dec 23 01:46:14 AEDT 2022


Hi Mark,

On Thu, 22 Dec 2022, Mark Clements wrote:

> Dear Mercury users,
> 
> Is there any way to write a function that takes a predicate and returns a transformed predicate without (a) naming a new predicate or (b) using a
> lambda? In practice, I would like to move the lambda to a function. However, the following code gives an error: "Cannot unify two terms of type
> `(pred float)'". Can I assume that this is outside of Mercury's mode system?

Unifying (or comparing) higher-order terms is not allowed by Mercury,
but that's not the problem here ...

> :- module tests.
> :- interface.
> :- import_module io.
> :- pred main(io::di, io::uo) is det.
> 
> :- implementation.
> :- import_module float, ranges, solutions.
> :- use_module math.
> 
> main(!IO) :-
>     test1(!IO), % OK
>     test2(!IO). % Does not compile
> 
> :- pred test1(io::di, io::uo) is det.
> test1(!IO) :-
>     Data = (pred(Y::out) is nondet :- nondet_member(I,range(1,5)), Y = float(I)), % some data
>     LnData = (pred(Y::out) is nondet :- Data(X), Y = math.ln(X)), % a transformation
>     aggregate(LnData, print_line, !IO).
> 
> :- pred test2(io::di, io::uo) is det.
> test2(!IO) :-
>     Data = (pred(Y::out) is nondet :- nondet_member(I,range(1,5)), Y = float(I)), % some data
>     LnData = f_ln(Data), %% This transformation does not compile:(
>     aggregate(LnData, print_line, !IO).
> 
> :- func f_ln(pred(float)::(pred(out) is nondet)) = (pred(float)::(pred(out) is nondet)).

This function declaration doesn't say what you think it does;
specifically the expanded mode of the function return value as written
is:

     pred(out) is nondet >> pred(out) is nondet

That's effectively an input, which is why the compiler is complaining
about attempted higher-order unifications.  What we want is for the
expanded mode of the return value to be:

     free >> pred(out) is nondet

That is: the return value has inst free before a call to f_ln and has
inst (pred(out) is nondet) after a call to it.

The above mode is more usually written as:

     out(pred(out) is nondet)

Putting it all together the declaration of f_ln should be:

    :- func f_ln(pred(float)::(pred(out) is nondet)) = (pred(float)::(out(pred(out) is nondet))).

> f_ln(Pred) = (pred(Y::out) is nondet :- Pred(X), Y is math.ln(X)).

As others have mentioned, Mercury does not support Prolog's is, just use
=.

Julien.


More information about the users mailing list