digitalmars.D - Misleading contract syntax
- Norbert Nemec (59/59) Mar 04 2010 Hi there,
- bearophile (21/32) Mar 04 2010 Thinking some more about it, I refuse what you say, or I don't understan...
- Norbert Nemec (41/100) Mar 04 2010 I think, this should be broken down into different issues:
- Norbert Nemec (15/28) Mar 04 2010 That's exactly why I plead for keeping apart the concepts "assertion"
- Michel Fortin (10/15) Mar 04 2010 I think a big problem today is that "in" contracts are enforced as part
- Ary Borenszweig (4/21) Mar 04 2010 int binary_search(int[] array, int n);
- =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= (2/25) Mar 04 2010 Why should you not be allowed helper functions?
- Ary Borenszweig (3/31) Mar 04 2010 Ok, but I don't want the language to force me to write helper functions
- Norbert Nemec (8/41) Mar 04 2010 Anything complex enough to need a helper function is very likely useful
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
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
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
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.Not good enough. Any kind of side-effect should be prohibited. Demanding as side-effect free expression is the simplest way to achieve this.* 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=3856Simply define a helper function. See other part of this thread.* 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...Why should a statement be simpler for the compiler than an expression? It could even be implemented as attribute, simplifying the syntax. Greetings, Norbert=> 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.
Mar 04 2010
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
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
On 03/04/2010 02:24 PM, Ary Borenszweig wrote:Norbert Nemec wrote:Why should you not be allowed helper functions?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
Pelle Månsson wrote:On 03/04/2010 02:24 PM, Ary Borenszweig wrote:Ok, but I don't want the language to force me to write helper functions just to write my contracts.Norbert Nemec wrote:Why should you not be allowed helper functions?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
Ary Borenszweig wrote:Pelle Månsson wrote: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.On 03/04/2010 02:24 PM, Ary Borenszweig wrote:Ok, but I don't want the language to force me to write helper functions just to write my contracts.Norbert Nemec wrote:Why should you not be allowed helper functions?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