Timeline for How to loosen input contracts by inheritance?
Current License: CC BY-SA 3.0
25 events
when toggle format | what | by | license | comment | |
---|---|---|---|---|---|
Oct 11, 2017 at 15:29 | comment | added | Frank Hileman | @NickL Maybe the circle/square discussion is actually correct in some places, but everywhere I have seen it used, it makes little sense, treating contracts as something vague, unspecified. Of course an invariant and postcondition are two separate things. | |
Oct 11, 2017 at 11:48 | comment | added | NickL | @FrankHileman I hadn't seen the wiki example yet, but indeed it is a bad example.. Of course there are some contracts there (the width and height of a square are equal), but the example is bad.. and probably wrong in a number of ways (e.g. "the square invariant would weaken the postconditions for Rectangle". This sounds wrong to me..) | |
Oct 10, 2017 at 16:31 | comment | added | Frank Hileman | The wikipedia article has a number of problems: 1) it is not a principle, but an idea. When using contracts, you cannot avoid the concept, as it is needed to perform type substitution. 2) the square-circle discussion is irrelevant (there are no contracts in that example). 3) Liskov and Guttag discussed this before Meyer's book was published, in "Abstraction and Specification in Program Development", 1986. | |
Oct 10, 2017 at 14:41 | history | tweeted | twitter.com/StackSoftEng/status/917761945784209408 | ||
Oct 9, 2017 at 18:55 | comment | added | inf3rno | @DavidPacker The example I posted was just made up and not an actual problem. I was curious about how is it possible to resuse tests by LSP. The short answer you cannot reuse contract tests, if the contracts change in the subclasses, so those tests won't hold, but that is not a problem, because they must not hold. You can reuse implementation tests for the inputs which are valid in the base class. For the inputs, which aren't valid in the base class, but are valid in the subclass you have to write new tests. | |
Oct 9, 2017 at 18:27 | comment | added | Andy | @NickL I was simply suggesting a different approach. I knew it was not related to the question, hence why I posted my suggestion as a comment only, rather than posting an actual answer. Even I when I am designing something I tend to get carried away and get blinded by my own implementation only to end up not seeing an alternative which may or may not be just as good or even better. I posted the alternative as a reminder that there are other approaches to the problem, too. | |
S Oct 9, 2017 at 18:15 | history | edited | inf3rno | CC BY-SA 3.0 |
added 2450 characters in body
|
Oct 9, 2017 at 17:52 | review | Suggested edits | |||
S Oct 9, 2017 at 18:15 | |||||
Oct 9, 2017 at 17:37 | comment | added | NickL | @DavidPacker OP is not dealing with any issue at all. His question was how is it possible to loosen preconditions while keeping the same behavior? His example is a made-up example to illustrate his interpretation of loosening preconditions. How do you plan to exemplify LSP without polymorphism? Your suggestion indeed is almost the same as OP's example, but more dynamic. However, how would that example be better for this question than OP's example? | |
Oct 9, 2017 at 16:43 | vote | accept | inf3rno | ||
Oct 9, 2017 at 16:04 | comment | added | Andy | @NickL I didn't miss the question. I also didn't think the problem is approached correctly with pointless inheritance and OP is dealing with this issue only because their class is not dynamic. My suggestion is not really that different from OP's example, with the exception of allowing you to freely extend the functionality when needed by injecting disallowed types. Thus yes, I think my proposed design is better - otherwise I wouldn't propose it. If you do not like the constructor injection you can use method arguments, that however kind of defeats the purpose of keeping the method API simple. | |
Oct 9, 2017 at 15:47 | comment | added | NickL | @DavidPacker I think you missed the questions. It was not about whether to use inheritance or not, but about the meaning of 'weakening preconditions' in the context of LSP. The purpose of the example is to have a sub-class weaken the precondition, without violating LSP. About your example, changing assertions based on the arguments supplied to the constructor? You really think that is a good design? Assertions assert whether the state is correct for the implementation of the function. How does the caller know what the function should assert? | |
Oct 9, 2017 at 9:15 | comment | added | Andy |
You do not need to use inheritance at all. By specifying available values through a list or a single variable like this you can then instantiate a new instance of T within your tests, either specifying some disallowed states or not. You are mostly suffering from poor design, not from violation of the LSP.
|
|
Oct 9, 2017 at 8:56 | comment | added | NickL | exactly! That is what the other answers also try to explain, but from different points of view. I've added my comments as an answer. | |
Oct 9, 2017 at 8:42 | answer | added | NickL | timeline score: 5 | |
Oct 9, 2017 at 4:55 | comment | added | inf3rno | @NickL Thanks I think I understand now. The whole point of LSP and these constraints what I wrote in my example. If we violate them, then the instances of the subclasses will cause unexpected errors in the code meant for the instances of the superclass. I can accept this if you want to post it as an answer. | |
Oct 8, 2017 at 23:14 | comment | added | NickL |
{ x >= 2 } x := x + 1 { x > 2} now with weaker precondition: { x >= 1 } x := x + 2 { x > 2} . Preconditions are assumptions you are allowed to make. Maybe you should take a look at Hoare Logic. >=1 contains more elements than AND contains the elements of >=2, thus it is weaker. The 'error' you mention only tests the assumption, since the function only provides valid output based on the assumption.
|
|
Oct 8, 2017 at 18:41 | comment | added | inf3rno | @NickL Ok. I read what I linked, I was right, that losening a precondition means that more arguments will be valid. So there was no misunderstanding about that. Can you write an example where you weaken a precondition and your postconditions still hold? Shouldn't a stronger precondition result in some kind of error by invalid values? | |
Oct 8, 2017 at 9:26 | answer | added | Bart van Ingen Schenau | timeline score: 3 | |
Oct 8, 2017 at 3:12 | comment | added | inf3rno | @NickL I think I might found a definition. cs.umd.edu/~mvz/handouts/weakest-precondition.pdf I am just too tired to think about what it means in this context. I'll check it tomorrow. | |
Oct 8, 2017 at 3:02 | comment | added | inf3rno | @NickL Probably you should add an answer with references about what weakening a precondition or postcondition means. I cannot judge currently whether you are right. I only found this reference, but I don't know where to find the article: en.wikipedia.org/wiki/Precondition#cite_note-2 | |
Oct 7, 2017 at 23:58 | comment | added | NickL | The reason why you can consider the assertion error as a postcondition is this case, is because you expect it to be the output. When you test a function, you use data that satisfies the precondition, call the function and verify whether the postcondition holds for the result. You can weaken the precondition, but should still ensure that the postcondition holds as well. | |
Oct 7, 2017 at 23:40 | comment | added | NickL |
By removing the assert you actually changed the postcondition. As you can see in your test, you check whether the postcondition .toThrow() holds. What the function says: precondition: given an input of "invalid", postcondition: throws assertionerror. You could weaken the precondition by always throwing an assertion error. Whay you are doing now us weakening the postcondition.
|
|
Oct 7, 2017 at 21:36 | answer | added | Patrick | timeline score: 2 | |
Oct 7, 2017 at 19:20 | history | asked | inf3rno | CC BY-SA 3.0 |