2024-05-18 19:16 AEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0000343mercuryBugpublic2014-07-02 10:07
Reporteraeonioshaplo 
Assigned Topbone 
PriorityhighSeveritymajorReproducibilityalways
StatusclosedResolutionno change required 
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 Files

-Relationships
+Relationships

-Notes

~0000723

juliensf (administrator)

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 (reporter)

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 (administrator)

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 (administrator)

For further information, see the Prolog to Mercury transition guide,
<http://www.mercurylang.org/information/doc-latest/mercury_trans_guide/index.html>.

~0000727

aeonioshaplo (reporter)

Last edited: 2014-07-01 14:34

View 2 revisions

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 (reporter)

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 (administrator)

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 (administrator)

Not a bug.
+Notes

-Issue History
Date Modified Username Field Change
2014-07-01 05:43 aeonioshaplo New Issue
2014-07-01 05:43 aeonioshaplo File Added: examples.tar.gz
2014-07-01 13:02 juliensf Note Added: 0000723
2014-07-01 13:33 aeonioshaplo Note Added: 0000724
2014-07-01 13:51 juliensf Note Added: 0000725
2014-07-01 13:57 juliensf Note Added: 0000726
2014-07-01 14:32 aeonioshaplo Note Added: 0000727
2014-07-01 14:34 aeonioshaplo Note Edited: 0000727 View Revisions
2014-07-01 16:48 aeonioshaplo Note Added: 0000728
2014-07-02 10:06 pbone Note Added: 0000729
2014-07-02 10:07 pbone Note Added: 0000730
2014-07-02 10:07 pbone Status new => closed
2014-07-02 10:07 pbone Assigned To => pbone
2014-07-02 10:07 pbone Resolution open => no change required
+Issue History