[mercury-users] Error detection rate in Mercury vs Other Languages (esp C++ and Python)

Peter Ross pro at missioncriticalit.com
Sat Dec 3 09:57:49 AEDT 2011

On 3 December 2011 04:12, Chris King <colanderman at gmail.com> wrote:
> On Fri, Dec 2, 2011 at 12:40 AM, Peter Ross <pro at missioncriticalit.com> wrote:
>> I do most of my development in Java and Mercury.  In Java it is much
>> more likely for the program to compile, but much more likely to throw
>> an exception the first time you run the program.
> I'd like to chime in though that Mercury's failure model lets a
> particularly pernicious class of bugs slip through easily; namely,
> those bugs in which you expect a result from some predicate but
> receive none, i.e., a predicate is failing for the wrong reason.  To
> give a specific example:
> % Set X -> Y in the map only if X < Y.
> :- func set_mapping(int, int, map(int, int)) = map(int, int) is semidet.
> set_mapping(X, Y, Map0) = map.insert(X, Y, Map0) :- X < Y.
> set_mapping(3, 5) will spuriously fail in the event that 3 was already
> in the map.  Obviously the programmer meant to use map.set (which
> can't fail) instead of map.update (which can fail), but Mercury
> doesn't catch this.  (Of course, one can use the declarative debugger
> to easily track down the bug, but that requires that the bug be
> caught, and failure bugs can be buried deep in a program and exhibit
> themselves subtly.)
> In Java and similar languages, where failure is often indicated by an
> exception (or null result, which would trigger an exception), the
> above error would be caught at compile time (assuming checked
> exceptions) or produce a backtrace at runtime.  Of course, the
> programmer could have wrapped the whole function in a try/catch which
> ignores all exceptions, but this is explicit and a well-known code
> smell.
[snip - example]

> This means that large "semidet" (or "nondet") procedures *without*
> either at least one declared "det" (or "multi") mode or prolific
> "require_det" checks should be considered a code smell in Mercury,
> just like explicitly ignoring exceptions is considered a code smell in
> Java.  Here I would say Java has a leg up on Mercury, because its code
> smell is in the *presence* of certain code, while Mercury's is in the
> *absence* of certain code.
I agree that semidet code is the hardest to write correctly because of
unexpected failures.  I have to admit that I rarely get caught out
with the examples you gave, mainly because I explicitly think for
every call whether or not it should fail, and call the version of the
library predicate.

What does catch me out is the following

map.det_lookup(Map, "key1", V)
... lots more code ...
map.det_lookup(Map, "key2", V)

Here both of my calls are det but there is an implicit unification
between the result of the two lookups.  Obviously V should be named V1
and V2.

One thing I would highly recommend though is to make semidet code
which depends on the cases of a type use a deterministic switch
followed by your semidet failure code.

:- type t
  ---> f
  ;     g.

require_det (
  ( X = f,
    Succeed = yes
  ; X = g,
    Succeed = no
Succeed = yes.

The reason I do this is that if at a later date you add a new
alternative type to t, you want the compiler to point out to you need
to handle the new alternative.

mercury-users mailing list
Post messages to:       mercury-users at csse.unimelb.edu.au
Administrative Queries: owner-mercury-users at csse.unimelb.edu.au
Subscriptions:          mercury-users-request at csse.unimelb.edu.au

More information about the users mailing list