Mercury Bugs - mercury
View Issue Details
0000343mercuryBugpublic2014-07-01 05:432014-07-02 10:07
Reporteraeonioshaplo 
Assigned Topbone 
PriorityhighSeveritymajorReproducibilityalways
StatusclosedResolutionno change required 
PlatformOSOS Version
Product Version 
Target VersionFixed in Version 
Summary0000343: Termination checking incorrect for disjunction
DescriptionIn certain cases where prolog-style logic programming is used, mmc will infer that a recursive pred like fib is multi or nondet when it should be det, especially when dealing with disjunctions.
Steps To Reproduceattempt to compile the attached examples fib_naive.m and fib_tail.m.
Additional InformationI'm including a third file, philosophy.m, which demonstrates a case where termination checking produces correct results against disjunction. In particular, writeStringsWithNLs/3 is of the same form as the above fib programs which cause it to fail.
TagsNo tags attached.
Attached Filesgz examples.tar.gz (878) 2014-07-01 05:43
https://bugs.mercurylang.org/file_download.php?file_id=218&type=bug

Notes
(0000723)
juliensf   
2014-07-01 13:02   
In fib_naive.m, the clause

   fib(In, Out) :- ...

will also match either fib(0, 0) or fib(1, 1), so the compiler is correct, the disjunction of those clauses
is multi rather than det. Likewise, for fib_tail where any input could match either of the two clauses
given. That is not the case for writeStringsWithNLs/3, where the first clauses will only match inputs with
an empty list and the second will only match inputs with a non-empty list.

In short, the compiler is correct here.
(0000724)
aeonioshaplo   
2014-07-01 13:33   
I don't believe that is correct, for example fib_naive could be stated equivalently as:

fib(In, Out) :-
    In is 0, Out is 0
    ; In is 1, Out is 1
    ; fib(In - 1, X), fib(In - 2, Y), Out is X + Y.

which are mutually exclusive and will only ever bind Out to one value in a given recursion. If both modes were ::out then it would be multi, but since one of the modes is ::in it should not be.
(0000725)
juliensf   
2014-07-01 13:51   
They are not mutually exclusive, if 'In' is 0, both the first clause will "succeed" and the third. (Operationally, it will probably go into loop due to 'In' being negative in the recursive call,
but that's not something determinism analysis deals with.)
By declaring this predicate to be 'det' you have effectively told the compiler that this will not be the case, hence the error. An actually det version of fib in Mercury would be written as follows:

    fib(In, Out) :-
       ( if In = 0 then
             Out = 0
       else if In =1 then
             Out = 1
       else
             fib(In - 1, X),
             fib(In -2 , Y),
             Out = X + Y
        ).
(0000726)
juliensf   
2014-07-01 13:57   
For further information, see the Prolog to Mercury transition guide,
<http://www.mercurylang.org/information/doc-latest/mercury_trans_guide/index.html>.
(0000727)
aeonioshaplo   
2014-07-01 14:32   
(Last edited: 2014-07-01 14:34)
I figured this out. This isn't a determinacy checking issue, it's a mode checking issue and the determinacy problem is just a symptom.

By declaring that In has mode ::in, I am stating that In should always be ground when the function is called (or I should get errors otherwise) and therefore the two unifications "X is 0" and "X is 1" can only be -comparisons- and not assignments. However, the compiler is inferring the other way around, and if you view them as assignments it makes sense that it would infer multi or nondet instead of det.

Also, if-then-else should not be required in logic since

if A then B else C

is in most cases equivalent to

head :- A, B; C.

which is more convenient and natural to write in a logic language. In cases where it isn't equivalent, mercury currently still makes it (more or less) equivalent by default. (referring to conjunctions with predicates that have !IO modes, which it refrains from reordering)

>>They are not mutually exclusive, if 'In' is 0, both the first clause will "succeed" and the third.

That's not how logic is supposed to work, AFAIK. If I write, for example:

head :- A; B; C.

B should only be evaluated if A fails, and C should only be evaluated if both A and B fail. If A succeeds B and C should never be called. Thus, an equivalent statement where C is evaluated would be:

head :- not(A ; B), C.
or
head :- not(A), not(B), C.

To be fair, it took me a pretty long time to understand the nuances of logic that well.

(0000728)
aeonioshaplo   
2014-07-01 16:48   
Nevermind, you were right and I was wrong. I forget that logical 'or' is not xor, and '0-1' does in fact produce a perfectly valid solution when the type is 'int'. That makes me wonder how the prolog version ever worked.

I can also see how if-then-else is useful, although I'm not entirely satisfied with that syntax. Or maybe just with the lack of an exclusive switch. I can't think of any more elegant way of avoiding the need for explicit negations, anyway.

At any rate I'd say it's safe to call this resolved, or failing that 'confused' works too. :P
(0000729)
pbone   
2014-07-02 10:06   
A possible point of confusion is that Mercury does support switches when a variable is unified with a distinct function symbol in each arm of a disjunction then the disjunction is a switch (exclusive). For example:

  (
    X = a,
    ...
  ;
    X = b,
    ...
  ).

However this currently does not work for numbers (it probably should) and often confuses new programmers. Additionally the compiler does not yet understand that comparisons can form exclusive and complete checks. Eg this doesn't work:

  (
    X > 0,
     ...
  ;
    X =< 0,
    ...
  ).

Also if you dislike the if-then-else syntax then there's another syntax:
http://www.mercurylang.org/information/doc-release/mercury_ref/Goals.html#Goals

Thanks.
(0000730)
pbone   
2014-07-02 10:07   
Not a bug.

Issue History
2014-07-01 05:43aeonioshaploNew Issue
2014-07-01 05:43aeonioshaploFile Added: examples.tar.gz
2014-07-01 13:02juliensfNote Added: 0000723
2014-07-01 13:33aeonioshaploNote Added: 0000724
2014-07-01 13:51juliensfNote Added: 0000725
2014-07-01 13:57juliensfNote Added: 0000726
2014-07-01 14:32aeonioshaploNote Added: 0000727
2014-07-01 14:34aeonioshaploNote Edited: 0000727bug_revision_view_page.php?bugnote_id=727#r27
2014-07-01 16:48aeonioshaploNote Added: 0000728
2014-07-02 10:06pboneNote Added: 0000729
2014-07-02 10:07pboneNote Added: 0000730
2014-07-02 10:07pboneStatusnew => closed
2014-07-02 10:07pboneAssigned To => pbone
2014-07-02 10:07pboneResolutionopen => no change required