# [mercury-users] Can this be done better?

Julien Fischer juliensf at csse.unimelb.edu.au
Tue Jan 31 01:03:52 AEDT 2012

```Hi,

On Mon, 30 Jan 2012, Michael Richter wrote:

> *Issue #1*
>
> I've decided to get serious learning Mercury and I'm doing so by filling in
> snippets at Rosetta Code <http://rosettacode.org>.  Today's
> exercise<http://rosettacode.org/wiki/First-class_functions#Mercury>has
> me a bit flummoxed.  While it works, at issue is an ugly third clause
> on the predicate providing the desired functionality.  Here's the code:
>
> :- module firstclass.
>
> :- interface.
> :- import_module io.
> :- pred main(io::di, io::uo) is det.
>
> :- implementation.
> :- import_module exception, list, math, std_util.
>
> main(!IO) :-
>   Forward = [math.sin, math.cos, (func(X) = math.ln(X))],
>   Reverse = [math.asin, math.acos, (func(X) = math.exp(X))],
>   apply(list.zip(Forward, Reverse), [], Results),
>   io.write_list(Results, ", ", io.write_float, !IO),
>   io.write_string("\n", !IO).
>
> :- pred apply(list((func(float) = float)), list(float), list(float)).
> :- mode apply(in, in, out) is det.
> apply([], A, Results) :- Results = A.
> apply([F, R|Functions], A, Results) :-
>   apply(Functions, [compose(R, F, 0.5) | A], Results).
> apply([_], _, _) :- throw("This can't happen!").
>
>
> The problem is that apply/3 without that third clause is deemed semidet
> because the second clause's head "can fail" (in that R may not always be
> bound according to the compiler).  Of course this is nonsense in this given
> situation because the list being passed in will *always* come in pairs, but
> naturally the compiler can't know that.  For me to get this code to compile
> I have to add that third clause (which will never be evaluated) just
> because.
>
> Now this bugs me because I know that Mercury can do better.  Indeed I
> suspect I'm missing something in the mode declaration that could tell the
> compiler that apply's first argument *must* have an even number of entries
> but I just can't figure out how to say it.
>
> So how would I go about getting rid of that third clause?

The compiler's support for inst subtyping like this is a bit flakey.
Nevertheless, it's possible to write a version of apply/3 that only
accepts input lists of even length -- depending on the circumstances
it may not be possible to call it however.

To begin with you need to define an inst that describes a list of even
length.  The trick here is define the evenlist inst in terms of a
mutually recursive oddlist inst.  For example,

:- inst evenlist == bound([] ; [ground | oddlist]).
:- inst oddlist  == bound([ground | evenlist])

Then the signature of apply/3 becomes:

:- pred apply(list((func(float) = float)), list(float), list(float)).
:- mode apply(in(evenlist), in, out) is det.

The compiler will now complain about the third clause being present,
although the error message it generates is not very nice.

Unfortunately, the above won't work since to use the new definition the
_compiler_ needs to be able to prove that the output of the call to
list.zip/2 also has the inst evenlist.  (Which is not possible given
the definition of zip/2.)

Rather than attemtping to use the above, the above can be more
compactly expressed in terms of things that already exist in
the standard library, for example:

main(!IO) :-
Forward = [math.sin, math.cos, (func(X) = math.ln(X))],
Reverse = [math.asin, math.acos, (func(X) = math.exp(X))],
Results = list.map_corresponding(
(func(F, R) = compose(R, F, 0.5)), Forward, Reverse),
io.write_list(Results, ", ", io.write_float, !IO),
io.write_string("\n", !IO).

or:

main(!IO) :-
Forward = [math.sin, math.cos, (func(X) = math.ln(X))],
Reverse = [math.asin, math.acos, (func(X) = math.exp(X))],
apply(assoc_list.from_corresponding_lists(Forward, Reverse),
[], Results),
io.write_list(Results, ", ", io.write_float, !IO),
io.write_string("\n", !IO).

:- pred apply(assoc_list((func(float) = float)), list(float),
list(float)).
:- mode apply(in, in, out) is det.

apply([], A, Results) :- Results = A.
apply([F - R | Functions], A, Results) :-
apply(Functions, [compose(R, F, 0.5) | A], Results).

Cheers,
Julien.
--------------------------------------------------------------------------
mercury-users mailing list
Post messages to:       mercury-users at csse.unimelb.edu.au