www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Misleading contract syntax

reply Norbert Nemec <Norbert Nemec-online.de> writes:
Hi there,

following the recent thread on contracts, I come to the conclusion that 
the current syntax is misleading and counterproductive.

* The concept of a contract is an expression, not a statement.
* A contract should *never* have side effects, otherwise debugging code 
may behave differently from release mode (which is the ultimate nightmare).
* Any reasonable example with contracts contains just a list of assertions.
* There is a fundamental distinction between assertions and contracts 
(see recent thread: contracts are part of interface design, assertions 
are computable comments)

=> Why are contracts defined as lists of statements in the first place? 
Defining contracts as side-effect free expressions would solve all of 
the above problems at once, decoupling the concepts of assertions and 
contracts and resulting in safer, much more concise code.

Just consider the example from 
http://en.wikipedia.org/wiki/Class_invariant#D rewritting using 
expressions as contracts. The strange "body" keyword becomes 
unnecessary. Inheritance of contracts can be achieved easily by 
concatenating in contract by 'or' operations and concatenating 
invariants and out contracts by 'and' operations.

-----------------

class Date {
     private int day, hour;

     invariant(1 <= day && day <= 31);
     invariant(0 <= hour && hour < 24);

     this(int d, int h) //constructor
         in (1 <= d && d <= 31 && 0 <= h && h < 24)
         out (day == d && hour == h)
         {
             day = d;
             hour = h;
         }

     public void setDay(int d)
         in (1 <= d && d <= 31)
         out (day == d)
         {
             day = d;
         }

     public void setHour(int h)
         in (0 <= h && h < 24)
         out (hour == h)
         {
             hour = h;
         }
}

void main() {
     Date d = new Date(/*d=*/10, /*h=*/5);
     d.setHour(30); // throws
}

----------------

B.t.w: to a useful, addition (orthogonal to the above change) would be 
access to the original state of the class in the out contract, e.g.:

----------------
     public void nextDay()
         out(day >= in.day)
         { day = day+1; }
----------------

Greetings,
Norbert
Mar 04 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Norbert Nemec:

 No! No! No! Design-by-contract means that it is the application's duty 
 to make sure that argument values are correct. If the program is 
 correct, the library can trust that the argument values are correct and 
 does not need to do checks. This is exactly the same situation that 
 assertions are for: Double-checking something to catch potential bugs.
Thinking some more about it, I refuse what you say, or I don't understand it still. If your library is very simple, then your application can know what input the function/class wants, but as soon as the library code becomes a little more complex, there can be corner input cases that the calling code doesn't know about, that the library can refuse with a good exception error. So generally the only piece of library code that knows (and needs to know) what are its correct inputs, is the library code itself. If you have to feed user interface inputs to some library code, you need to clean and test such inputs well. But if the library is not designed to perform tests for input data, then you need two pieces of code: the library code, plus another piece of code that knows everything about that library code itself, to test the inputs well. This is very bad design.
A library interface simply is something different than a user interface.<
I don't buy this. Complex enough different parts of the same program need "user interfaces" for each other, where the user is the programmer or another part of the program written by the programmer or another programmer. So libraries need to test all their inputs well anyway. I have programmed for enough years to be sure of this. So using preconditions based on asserts is not enough. ------------------- Brad Roberts:
Asserts in the body of a method have no impact on overridden implementations in
subclasses, but in/out contracts do.  So there's a distinct value in using
in/out for non-final methods.<
I like class/struct invariants, I like postconditions, I can probably appreciate loop invariants too. All of them test for code correctness, because they have to fire only if there's a bug in the program. But I don't like preconditions done with asserts, because any part of the program can receive wrong inputs from any other part of the code. So in functions/methods of my programs I will keep using exceptions to test inputs. So overridden implementations in subclasses will need to know what are their good inputs. ------------------- Norbert Nemec:
* A contract should *never* have side effects, otherwise debugging code may
behave differently from release mode (which is the ultimate nightmare).<
I agree, but I have asked for something a little less strong, because I think it's enough and doesn't limit freedom too much: http://d.puremagic.com/issues/show_bug.cgi?id=3856
* Any reasonable example with contracts contains just a list of assertions.<
I don't agree. There are many situations where you want to perform more computations, for example if a function needs a sorted input, you need a loop (or a call to a Phobos function like isSorted) to test if it's actually sorted.
=> Why are contracts defined as lists of statements in the first place?<
Probably because Walter follows the very good engineering advice of keeping things simple. The current design is probably the simpler thing for the compiler, and it's good enough. I don't think the syntax needs to be changed.
B.t.w: to a useful, addition (orthogonal to the above change) would be access
to the original state of the class in the out contract, e.g.:<
I agree it's useful and I'd like to have it, but it seems it's not easy to design it well: http://www.digitalmars.com/d/archives/digitalmars/D/Communicating_between_in_and_out_contracts_98252.html Bye, bearophile
Mar 04 2010
next sibling parent Norbert Nemec <Norbert Nemec-online.de> writes:
I think, this should be broken down into different issues:

* Is real-life code ever stable enough to take deactivate assertion checks?

I think that there is no simple yes/no question. The best solution would 
probably be to allow selectively deactivating assertions. It may indeed 
be advisable to leave the contract checks between library and 
application activated even when the software is shipped. Even within a 
library, it might make sense to group the assertions into different 
levels that one can activate or deactivate in groups.


* How do contracts fit in?

The interface of a library should fully define the legal set of input 
and output values. Of course, this is difficult to do formally for 
complex libraries, but if you like, you can always call "exceptions" a 
legal form of output. For many cases, these may even be the most 
convenient form. Think, e.g. about numerical instabilities that occur 
deep inside complex algorithms.

So, typically, contracts handle the simple cases that can be checked 
easily. Difficult inputs that cannot be checked easily, on the other 
hand, should simply be considered "legal" by the contract, triggering an 
exception, which also is a perfectly legal output.

Assertions, on the other hand, should never be used in that way. An 
assertion is about as informal as a comment. A broken assertion tells me 
that whoever wrote this "comment" obviously made an incorrect assumption 
and there must be a bug somewhere within the library. Possibly, the bug 
is in the library interface definition, which may need to tighten its 
contracts and thereby require changes in the application code, but 
still: a broken assertion is *always* a bug in the code that contains 
the assertion.


* How far can the library programmer trust the application programmer?

The library programmer does not need to trust anyone. It is the 
application programmer who is ultimately responsible that the program 
works as promised. So the application programmer has to decide whether 
he trusts himself enough to take out the contract checks.

In a user interface, there is effectively never the situation that the 
user is confident enough in his typing ability to take out security 
checks. That is why languages typically do not provide means to 
deactivate them.

In library interfaces, on the other hand, this state can be reached by 
careful coding and sufficient testing. Many project may never reach it 
due to their complexity, but some (parts of) software can certainly get 
there and switch off the checks at the interfaces in release mode.





bearophile wrote:
 Norbert Nemec:
 
 No! No! No! Design-by-contract means that it is the application's duty 
 to make sure that argument values are correct. If the program is 
 correct, the library can trust that the argument values are correct and 
 does not need to do checks. This is exactly the same situation that 
 assertions are for: Double-checking something to catch potential bugs.
Thinking some more about it, I refuse what you say, or I don't understand it still. If your library is very simple, then your application can know what input the function/class wants, but as soon as the library code becomes a little more complex, there can be corner input cases that the calling code doesn't know about, that the library can refuse with a good exception error. So generally the only piece of library code that knows (and needs to know) what are its correct inputs, is the library code itself. If you have to feed user interface inputs to some library code, you need to clean and test such inputs well. But if the library is not designed to perform tests for input data, then you need two pieces of code: the library code, plus another piece of code that knows everything about that library code itself, to test the inputs well. This is very bad design.
 A library interface simply is something different than a user interface.<
I don't buy this. Complex enough different parts of the same program need "user interfaces" for each other, where the user is the programmer or another part of the program written by the programmer or another programmer. So libraries need to test all their inputs well anyway. I have programmed for enough years to be sure of this. So using preconditions based on asserts is not enough. ------------------- Brad Roberts:
 Asserts in the body of a method have no impact on overridden implementations
in subclasses, but in/out contracts do.  So there's a distinct value in using
in/out for non-final methods.<
I like class/struct invariants, I like postconditions, I can probably appreciate loop invariants too. All of them test for code correctness, because they have to fire only if there's a bug in the program. But I don't like preconditions done with asserts, because any part of the program can receive wrong inputs from any other part of the code. So in functions/methods of my programs I will keep using exceptions to test inputs. So overridden implementations in subclasses will need to know what are their good inputs. ------------------- Norbert Nemec:
 * A contract should *never* have side effects, otherwise debugging code may
behave differently from release mode (which is the ultimate nightmare).<
I agree, but I have asked for something a little less strong, because I think it's enough and doesn't limit freedom too much: http://d.puremagic.com/issues/show_bug.cgi?id=3856
 * Any reasonable example with contracts contains just a list of assertions.<
I don't agree. There are many situations where you want to perform more computations, for example if a function needs a sorted input, you need a loop (or a call to a Phobos function like isSorted) to test if it's actually sorted.
 => Why are contracts defined as lists of statements in the first place?<
Probably because Walter follows the very good engineering advice of keeping things simple. The current design is probably the simpler thing for the compiler, and it's good enough. I don't think the syntax needs to be changed.
 B.t.w: to a useful, addition (orthogonal to the above change) would be access
to the original state of the class in the out contract, e.g.:<
I agree it's useful and I'd like to have it, but it seems it's not easy to design it well: http://www.digitalmars.com/d/archives/digitalmars/D/Communicating_between_in_and_out_contracts_98252.html Bye, bearophile
Mar 04 2010
prev sibling parent reply Norbert Nemec <Norbert Nemec-online.de> writes:
bearophile wrote:
 Complex enough different parts of the same program need "user interfaces" for
each other, where the user is the programmer or another part of the program
written by the programmer or another programmer. So libraries need to test all
their inputs well anyway. I have programmed for enough years to be sure of this.
 So using preconditions based on asserts is not enough.
That's exactly why I plead for keeping apart the concepts "assertion" and "contract". It should be possible to deactivate the assertions inside a library and still keep the contracts active.
 But I don't like preconditions done with asserts, because any part of the
program can receive wrong inputs from any other part of the code. So in
functions/methods of my programs I will keep using exceptions to test inputs.
Exactly my point: contracts are not assertions. Still, contracts are not exceptions either. A broken contract is a BUG. A triggered exception is a run-time condition that can happen in a perfectly correct program.
 * A contract should *never* have side effects, otherwise debugging code may
behave differently from release mode (which is the ultimate nightmare).<
I agree, but I have asked for something a little less strong, because I think it's enough and doesn't limit freedom too much: http://d.puremagic.com/issues/show_bug.cgi?id=3856
Not good enough. Any kind of side-effect should be prohibited. Demanding as side-effect free expression is the simplest way to achieve this.
 * Any reasonable example with contracts contains just a list of assertions.<
I don't agree. There are many situations where you want to perform more computations...
Simply define a helper function. See other part of this thread.
 => Why are contracts defined as lists of statements in the first place?<
Probably because Walter follows the very good engineering advice of keeping things simple. The current design is probably the simpler thing for the compiler, and it's good enough. I don't think the syntax needs to be changed.
Why should a statement be simpler for the compiler than an expression? It could even be implemented as attribute, simplifying the syntax. Greetings, Norbert
Mar 04 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-03-04 13:19:25 -0500, Norbert Nemec <Norbert Nemec-online.de> said:

 Exactly my point: contracts are not assertions.
 
 Still, contracts are not exceptions either. A broken contract is a BUG. 
 A triggered exception is a run-time condition that can happen in a 
 perfectly correct program.
I think a big problem today is that "in" contracts are enforced as part of the function. They should be checked at the call site because "in" contract check for bugs in the calling code. That would make all entry points to a library validated when the calling code is compiled in debug mode, even if the library is compiled in release mode. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Mar 04 2010
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Norbert Nemec wrote:
 Hi there,
 
 following the recent thread on contracts, I come to the conclusion that 
 the current syntax is misleading and counterproductive.
 
 * The concept of a contract is an expression, not a statement.
 * A contract should *never* have side effects, otherwise debugging code 
 may behave differently from release mode (which is the ultimate nightmare).
 * Any reasonable example with contracts contains just a list of assertions.
 * There is a fundamental distinction between assertions and contracts 
 (see recent thread: contracts are part of interface design, assertions 
 are computable comments)
 
 => Why are contracts defined as lists of statements in the first place? 
 Defining contracts as side-effect free expressions would solve all of 
 the above problems at once, decoupling the concepts of assertions and 
 contracts and resulting in safer, much more concise code.
int binary_search(int[] array, int n); Write the precondition of this function using only ors and ands (and no helper functions).
Mar 04 2010
parent reply =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
On 03/04/2010 02:24 PM, Ary Borenszweig wrote:
 Norbert Nemec wrote:
 Hi there,

 following the recent thread on contracts, I come to the conclusion
 that the current syntax is misleading and counterproductive.

 * The concept of a contract is an expression, not a statement.
 * A contract should *never* have side effects, otherwise debugging
 code may behave differently from release mode (which is the ultimate
 nightmare).
 * Any reasonable example with contracts contains just a list of
 assertions.
 * There is a fundamental distinction between assertions and contracts
 (see recent thread: contracts are part of interface design, assertions
 are computable comments)

 => Why are contracts defined as lists of statements in the first
 place? Defining contracts as side-effect free expressions would solve
 all of the above problems at once, decoupling the concepts of
 assertions and contracts and resulting in safer, much more concise code.
int binary_search(int[] array, int n); Write the precondition of this function using only ors and ands (and no helper functions).
Why should you not be allowed helper functions?
Mar 04 2010
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Pelle Månsson wrote:
 On 03/04/2010 02:24 PM, Ary Borenszweig wrote:
 Norbert Nemec wrote:
 Hi there,

 following the recent thread on contracts, I come to the conclusion
 that the current syntax is misleading and counterproductive.

 * The concept of a contract is an expression, not a statement.
 * A contract should *never* have side effects, otherwise debugging
 code may behave differently from release mode (which is the ultimate
 nightmare).
 * Any reasonable example with contracts contains just a list of
 assertions.
 * There is a fundamental distinction between assertions and contracts
 (see recent thread: contracts are part of interface design, assertions
 are computable comments)

 => Why are contracts defined as lists of statements in the first
 place? Defining contracts as side-effect free expressions would solve
 all of the above problems at once, decoupling the concepts of
 assertions and contracts and resulting in safer, much more concise code.
int binary_search(int[] array, int n); Write the precondition of this function using only ors and ands (and no helper functions).
Why should you not be allowed helper functions?
Ok, but I don't want the language to force me to write helper functions just to write my contracts.
Mar 04 2010
parent Norbert Nemec <Norbert Nemec-online.de> writes:
Ary Borenszweig wrote:
 Pelle Månsson wrote:
 On 03/04/2010 02:24 PM, Ary Borenszweig wrote:
 Norbert Nemec wrote:
 Hi there,

 following the recent thread on contracts, I come to the conclusion
 that the current syntax is misleading and counterproductive.

 * The concept of a contract is an expression, not a statement.
 * A contract should *never* have side effects, otherwise debugging
 code may behave differently from release mode (which is the ultimate
 nightmare).
 * Any reasonable example with contracts contains just a list of
 assertions.
 * There is a fundamental distinction between assertions and contracts
 (see recent thread: contracts are part of interface design, assertions
 are computable comments)

 => Why are contracts defined as lists of statements in the first
 place? Defining contracts as side-effect free expressions would solve
 all of the above problems at once, decoupling the concepts of
 assertions and contracts and resulting in safer, much more concise 
 code.
int binary_search(int[] array, int n); Write the precondition of this function using only ors and ands (and no helper functions).
Why should you not be allowed helper functions?
Ok, but I don't want the language to force me to write helper functions just to write my contracts.
Anything complex enough to need a helper function is very likely useful to have anyway, e.g. to allow the library user to check for the condition before calling the function. (Remember: the contract check is just a safety net. It still is the caller's responsibility to make sure that the input is legal before calling the function) Apart from that: an occasional helper function causes far less clutter than the current syntax with all the "assert" keywords in each contract.
Mar 04 2010