March 11, 2016 Re: Suggested Change to Contract Syntax | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Friday, 11 March 2016 at 01:45:36 UTC, Jonathan M Davis wrote:
>
> Sure, but if you're not using in/out contracts with a class, you're not going to see how they interact with inheritance. To mimic what they do, you'd have to duplicate the base class contracts in the derived class and make sure that you ||ed the in contracts correctly and &&ed the out contracts correctly, which isn't very maintainable.
>
> - Jonathan M Davis
If I'm understanding you correctly, you could still get the same behavior with something like below (though it isn't exactly right since Base.foo's in block would technically have a funky rule applied to it).
After playing around with your example, I'm finding in/out blocks on derived classes to be tricky. I think I'm going to try to avoid putting myself in a situation where I would screw something up.
import core.exception;
import std.exception;
class Base
{
void baseCheck(int i)
{
assert(i % 2 == 0, "base out failed");
}
int foo(int i)
{
assert(i > 0 && i < 10, "base in failed");
scope(exit) baseCheck(i);
return i;
}
}
class Derived : Base
{
override int foo(int i)
{
scope(exit) baseCheck(i);
assert(i < 50, "derived in failed");
scope(exit) assert(i % 3 == 0, "derived out failed");
return i;
}
}
| |||
March 11, 2016 Re: Suggested Change to Contract Syntax | ||||
|---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Friday, 11 March 2016 at 04:17:51 UTC, jmh530 wrote: > On Friday, 11 March 2016 at 01:45:36 UTC, Jonathan M Davis wrote: >> >> Sure, but if you're not using in/out contracts with a class, you're not going to see how they interact with inheritance. To mimic what they do, you'd have to duplicate the base class contracts in the derived class and make sure that you ||ed the in contracts correctly and &&ed the out contracts correctly, which isn't very maintainable. >> >> - Jonathan M Davis > > If I'm understanding you correctly, you could still get the same behavior with something like below (though it isn't exactly right since Base.foo's in block would technically have a funky rule applied to it). Yeah, you can do something like that and get it to work, but it gets increasingly tricky, the more complicated the contracts are, and it's pretty easy to screw it up. Certainly, it's far cleaner and less error-prone to have it built into the language like we do. > After playing around with your example, I'm finding in/out blocks on derived classes to be tricky. I think I'm going to try to avoid putting myself in a situation where I would screw something up. Yeah. Having complicated contracts is probably a bad idea in general, but it gets far worse when inheritance is involved. And you can give yourself some weird problems even with the built-in help that D gives you. For instance, in my example, the derived class' in contract was a looser version of the base class' in contract, which is usually what you'd be looking to do, but the way it works is that the bass class' in contract and the derived class' in contract are ||ed. So, technically, you could make it so that the two contracts are completely distinct or so that the derived class' in contract is tighter, but what you ultimately end up with is the two in contracts ||ed, whereas what most folks will probably expect at a glance is that the derived class' contract will be met. So, doing something like base class: assert(i < 50); derived class: assert(i < 10); or bass class: assert(i < 50); derived class: assert(i > 50); will quickly make it so that you could have a derived class function which expects the derived class' in contract to be pass when it doesn't, because what's tested is the ||ing or the contracts not just the derived class contract. So, ultimately, you still have to be familiar with how the in and out contracts are supposed to work with inheritance to avoid shooting yourself in the foot, but having it built in makes it so that it's harder to screw it up. It just doesn't fix the whole problem for you. Regardless, I'd be _very_ careful with contracts and inheritance, and having complicated contracts with inheritance seems a bit suicidal. - Jonathan M Davis | |||
March 11, 2016 Re: Suggested Change to Contract Syntax | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 2016-03-10 23:57, Jonathan M Davis wrote: > IMHO, at this point, inheritance is the only reason they're worth having > in the language. Without inheritance, in contracts could just as easily > be assertions at the top of the function, and while out contracts are > certainly easier as they are now rather than having to put a contract at > each return statement or use a scope(exit) statement to do the out > contracts, you can still do out contracts that way, and honestly, I > don't think that out contracts are worth much anyway, since in almost > all cases, unit tests do that job already, and it's usually much easier > to write test that specific input gives specific output than it is to > have a general test at the end of the function. > > But while in most cases, in and out contracts are trivially done in the > function itself, inheritance is a whole other kettle of fish, and having > them built into the language solves that problem whereas doing it > yourself is actually fairly hard and error-prone. So, for that reason, > and that reason alone, I think that having them built into the language > is a good thing. > > There has been some discussion of getting the compiler to catch some > stuff based on in contracts, in which case, in contracts would be worth > a bit more, but as it stands, I pretty much never use out contracts > (because I think that they're useless), and if inheritance isn't > involved (which it usually isn't), then I don't even bother with an in > contract and just put the assertions in the body and avoid the extra > syntax. If/Once we get more features in the compiler which take > advantage of contracts, then I'll likely change my tune on that one, but > for now, IMHO, they're really only of value in classes. Another advantage of the contracts would be if the caller would be responsible for running the contracts. That is currently not the case. Then a library could be shipped with contracts and it's up to the user of the library to decide if the contracts should be executed or not. I don't want to start a discussion which approach is right and which is wrong. -- /Jacob Carlborg | |||
March 11, 2016 Re: Suggested Change to Contract Syntax | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Friday, 11 March 2016 at 10:22:06 UTC, Jacob Carlborg wrote:
> Another advantage of the contracts would be if the caller would be responsible for running the contracts. That is currently not the case. Then a library could be shipped with contracts and it's up to the user of the library to decide if the contracts should be executed or not.
The biggest problem with associating the contract with the source rather than the caller is that you only get those assertions when that library was built with assertions enabled, whereas in contracts are really testing the caller code, so what you really want is for them to be enabled or not depending on whether the calling code is compiled with assertions enabled or not. So, having the in contracts are compiled in based on the calling code would be huge IMHO (and certainly make using explicit in contracts very valuable), but there are definitely implementation issues with pulling that off. Right now, in contracts are basically just the beginning part of the function, whereas they need to be separate from it for the caller to be able to determine whether they're enabled or not, and how to do that in a way that works with our calling conventions is not immediately obvious.
Whether out contracts or invariants should be compiled in based on the caller is far less obvious though, since they're really testing the code that they're in, whereas in contracts are really testing the caller.
Regardless, if in contracts were improved to be compiled in or not based on how the calling code is compiled would definitely make it so that I'd use in contracts over just putting the assertions at the top of the function.
- Jonathan M Davis
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply