views:

27

answers:

2

I wander why Mercury (10.04) can't infer determinism of next snippet:

:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
    open_input(cpu_fn(CPU, "available_frequencies"), ResStream, !IO),
    (ResStream = io.ok(Stream) ->
        ResFreqs = io.ok([])
    ;ResStream = io.error(Err),
        ResFreqs = io.error(Err)
    ).

It complains:

cpugear.m:075: In `load_freqs'(in, out, di, uo):
cpugear.m:075:   error: determinism declaration not satisfied.
cpugear.m:075:   Declared `det', inferred `semidet'.
cpugear.m:080:   Unification of `ResStream' and `io.error(Err)' can fail.
cpugear.m:076: In clause for predicate `cpugear.load_freqs'/4:
cpugear.m:076:   warning: variable `CPU' occurs only once in this scope.
cpugear.m:078: In clause for predicate `cpugear.load_freqs'/4:
cpugear.m:078:   warning: variable `Stream' occurs only once in this scope.

But io.res have only io.ok/1 and io.error/1.
And next snippet of code compiles well:

:- pred read_freqs(io.res(io.input_stream)::in, io.res(list(float))::out, io::di, io::uo) is det.
read_freqs(io.ok(Stream), io.ok([]), IO, IO).
read_freqs(io.error(Err), io.error(Err), IO, IO).

Update #1: It can decide det even for:

:- pred read_freqs(bool::in, io.res(io.input_stream)::in, io.res(list(float))::out, io::di, io::uo) is det.
read_freqs(no, ResStream, io.ok([]), IO, IO):- ResStream = io.ok(_).
read_freqs(F, io.ok(_), io.ok([]), IO, IO):- F = yes.
read_freqs(yes, io.error(Err), io.error(Err), IO, IO).
read_freqs(F, ResStream, io.error(Err), IO, IO):- ResStream = io.error(Err), F = no.
A: 

My reading of the Mercury rules for determinism with conditionals (below) is that for this to be considered deterministic, you should replace the -> with a ,

From the Mercury reference manual:

If the condition of an if-then-else cannot fail, the if-then-else is equivalent to the conjunction of the condition and the “then” part, and its determinism is computed accordingly. Otherwise, an if-then-else can fail if either the “then” part or the “else” part can fail.

RD1
In this case `ResStream` may unify with `io.ok(Stream)`, but also may unify with `io.error(Err)`. That's depends on result of opening file. There is two branches for that. To get exclusive "or" I use "if-the-else" construction and as according to your citation it should suit. But Mercury can't infer that `not(ResStream = ok(_)) => ResStream = error(_)`.
ony
In your case, the else branch is `ResStream = io.error(Err), ResFreqs = io.error(Err)` which will fail if ResStream is io.ok(X). The rule I quoted doesn't say anything about adding the negation of the condition. I agree that logically it could be added, but the reference manual indicates that it isn't. In contrast, disjunctions are treated specially if they have the form of a "switch" - which seems to allow conjunction in the disjuncts but not conditionals. I guess the reasoning is that in the common case the patterns don't intersect, and in that case "," and "->" do the same thing.
RD1
Patterns can intersect and Mercury finds those intersections with infering determinism "multi". See additional example. If you will decide det. only by head than `read_freqs` (in update) will be marked as multi.
ony
A: 

Ok, it can infer det for:

:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
    open_input(cpu_fn(0, "available_frequencies"), ResStream, !IO),
    (ResStream = io.ok(Stream),
        ResFreqs = io.ok([])
    ;ResStream = io.error(Err),
        ResFreqs = io.error(Err)
    ).

But why "if-then-else" construction introduces semidet?

ony
My first answer to "why?" is because that's what the Mercury reference manual says should happen. But, I guess you mean "why is Mercury designed that way?". My guess is that switches need to be treated in a special way, and the designers didn't want to make them any more complicated than necessary, and avoiding negative information really simplifies things. Plus, my impression is that Mercury generally encourages switches in preference to conditionals. Indeed there's a switch --inform-ite-instead-of-switch to enable reporting of conditionals that should be replaced by switches.
RD1
Thanks, that's useful option "--inform-ite-instead-of-switch".
ony