mercury

In Mercury, why can't I use a state variable in a function application?


When describing state variables, the Mercury reference manual says: "!X may not appear as an argument in a function application, since this would not make sense given the usual interpretation of state variables and functions" (p. 14). I'd like to understand that reasoning better: What is it about state variables and functions that makes the combination nonsensical?

Part of my confusion comes from the code below, where I'm able to use the !.X, !:X form of state variables, but not the !X form. If I can use the former, why not the latter?

This code compiles, and runs as expected:

% Choose values, A and B, from the list of values, subject to certain restrictions
:- pred pick_a_b(int, int, list(int), list(int)).
:- mode pick_a_b(out, out, in,        out) is nondet.
pick_a_b(A, B, !Values) :-
    A = pick(!.Values, !:Values),
    B = pick(!.Values, !:Values),
    A \= 3, A > 2, B > 2, B \= 4.

% Choose any item from the input list; pass all the un-chosen items back to the caller.
:- func pick(list(int), list(int)) = int.
:- mode pick(in,        out)       = out is nondet.
pick([X | Xs], Xs)      = X.
pick([X | Xs], [X | Zs]) = pick(Xs, Zs).

main(!IO) :-
    if 
        Values = [1, 2, 3, 4, 5],
        pick_a_b(A, B, Values, _)
    then
        io.format("A = %i\n", [i(A)], !IO),
        io.format("B = %i\n", [i(B)], !IO)
    else
        io.format("Something went wrong\n", [], !IO).

But changing the first two lines of pick_a_b results in compile errors.

pick_a_b(A, B, !Values) :-
    A = pick(!Values),
    B = pick(!Values),
    A \= 3, A > 2, B > 2, B \= 4.

Solution

  • Your "function" pick has a strange mode, (in, out) = out. This is very unusual and this is why the !:Values syntax works in your first example. It normally wouldn't as functions normally have the mode (in, in, ..., in) = out.

    The reference manual is correct in saying that the !X syntax doesn't make sense in a function call, I also believe that your use of !:Values and your function's mode do not make sense either. Yes your code is legal and the compiler accepts it and can compile it, however I don't know why anyone would use a function here rather than a predicate. Doing so is likely to confuse people reading your code in the future.