[m-dev.] Declarative debugger and pre and post conditions

Ian MacLarty maclarty at cs.mu.OZ.AU
Sun Oct 3 17:24:33 AEST 2004


Originally my idea was to be able to write compilable specifications 
which would just be a version of the predicate with the modes of all 
the arguments being `in'.  To satisfy its specification a predicate 
should be logically equivalent to its specification.  For example the 
spec for a sort predicate might look like:

sort_spec(A, B) :- list.perm(A, B), ordered(B).

where ordered/1 just checked that the elements were in order.  Having 
all the arguments being input gives us maximum expressive power when 
writing the specification.

However these types of specifications do not in general help the 
declarative degubber work out if an invalid call is inadmissible (a 
pre-condition is violated) or erroneous (a post-condition is violated). 
  This information is important to the declarative debugger because it 
tells it in which part of the call tree the bug might be.  In fact the 
specification mechanism I've mentioned above wouldn't even cover the 
concept of inadmissibility.  A call would either be correct or 
incorrect and if it was incorrect there'd be no way in general to work 
out if it was the inputs that made the call incorrect and thus 
inadmissible.

There are also similar problems with the weaker "requirements" 
suggestion.  For example suppose we had the following requirements on a 
predicate p:

:- pragma ensure(p(A, B), (A < B, B =< 10)).

Now suppose the call p(10, 11) succeeded where 10 was input and 11 was 
output.  Clearly the requirements have been violated, but if we use the 
suggested method of assuming that requirements that mention only inputs 
are preconditions and requirements that mention outputs are 
postconditions, then we'd infer that a postcondition was violated 
(since the requirement B =< 10 was violated).  However if we intended p 
not to be called with it's first argument >= 10, then a precondition 
has been violated.  We could explicitly add this as a requirment : (A < 
B, B =< 10, A < 10), but this seems redundant as it's already implied 
by the existing requirments.

I think it's important to seperate out pre and post conditions so it's 
plain whether these are conditions the caller or the callee should 
fulfil.  A specification mechanism needs to seperate these two concepts 
out so the declarative debugger can work out if it's the caller or the 
callee that's not doing the right thing.

I therefore support the idea of each mode of a predicate having it's 
own set of preconditions.  Each precondition would be a statement about 
the `in' arguments in that mode.  This makes it clear that this is an 
extra condition we require the caller to adhere to when calling the 
predicate in that mode.  This also reflects the reason for needing a 
preconditon in the first place : the implementation of the predicate is 
only designed to work for calls who's inputs satisfy a certain 
condition, which is similar to the reason we have mode declarations - 
with a mode declaration we are saying the implementation of the 
predicate is only designed to work when certain arguments are ground.

This may lead to preconditions for different modes contradicting each 
other, but I don't see that as a problem in real applications as most 
predicates tend to only have one mode anyway.  Also modes could be 
grouped together under one precondition as long as the arguments in the 
preconditon are `in' for all the modes the precondition applies to.

Post conditions wouldn't need to be mode specific and could be 
expressed much like the specification mechanism shown before, except 
they would only apply when the call succeeded, so they wouldn't be 
logically equivalent to the predicate - they would just be implied by 
the predicate.

I don't see this mechanism being used as a runtime assertion checking 
mechanism that would throw an exception when a call doesn't satisfy 
it's pre or post conditions.  I don't think a specification mechanism 
should change the semantics of the program it's specifying.  Instead 
the program could be compiled with a --check-conditions flag and the 
generated executable would run normally, but would produce a report 
afterwards saying which calls violated their pre and post conditions.  
Runtime assertion checking that throws an exception can be done with 
require.require/2.

Ian.

--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list