www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do I use in contract with interface?

reply Dr. Assembly <netorib94 gmail.com> writes:
I'm learning to use interface with contracts. In below code, in 
isn't being "called". Can someone point out why? what am I doing 
wrong?

void main() {
	C c = new C();
	writeln(c.foo(1));
}

interface I
{
	int foo(int i)
		in { assert(i > 2); }
		out (result) { assert(result != 0); }

	void baa();
}

class C : I
{
	int foo(int i) {
		return i * 2;
	}

	void baa() {
		writeln("Hello!");
	}
}
Nov 15 2017
next sibling parent Dr. Assembly <netorib94 gmail.com> writes:
out(result) {}

run fine, if foo() return 0, an exception is throw by assert() 
but in doesn't work. I don't know what I'm missing. It' my first 
time using contracts
Nov 15 2017
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/15/17 9:43 AM, Dr. Assembly wrote:
 I'm learning to use interface with contracts. In below code, in isn't 
 being "called". Can someone point out why? what am I doing wrong?
 
 void main() {
      C c = new C();
      writeln(c.foo(1));
 }
 
 interface I
 {
      int foo(int i)
          in { assert(i > 2); }
          out (result) { assert(result != 0); }
 
      void baa();
 }
 
 class C : I
 {
      int foo(int i) {
          return i * 2;
      }
 
      void baa() {
          writeln("Hello!");
      }
 }
Not going to defend these design decisions, but according to https://dlang.org/spec/contracts.html#in_out_inheritance: 1. If a function in a derived class overrides a function in its super class, then only one of the in contracts of the function and its base functions must be satisfied. Overriding functions then becomes a process of loosening the in contracts. 2. A function without an in contract means that any values of the function parameters are allowed. This implies that if any function in an inheritance hierarchy has no in contract, then in contracts on functions overriding it have no useful effect. So simply overriding a function and specifying no in contract cancels all in contracts. I don't think there's a way to specify inheriting the in contract. I believe the only correct way to do this is to repeat the base's in contract. -Steve
Nov 15 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 15, 2017 10:14:32 Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On 11/15/17 9:43 AM, Dr. Assembly wrote:
 I'm learning to use interface with contracts. In below code, in isn't
 being "called". Can someone point out why? what am I doing wrong?

 void main() {

      C c = new C();
      writeln(c.foo(1));

 }

 interface I
 {

      int foo(int i)
          in { assert(i > 2); }
          out (result) { assert(result != 0); }

      void baa();

 }

 class C : I
 {

      int foo(int i) {
          return i * 2;
      }

      void baa() {
          writeln("Hello!");
      }

 }
Not going to defend these design decisions, but according to https://dlang.org/spec/contracts.html#in_out_inheritance:
Well, the rules do make sense when you consider that it can make sense for a function to allow arguments that the base class didn't. The in contract is essentially saying what the function can validly operate on, and there's no reason why a derived class couldn't work with more values than the base class could. So, the loosening makes sense when you think about it. It just means that if you're trying to have a contract that you put in one place and then gets reused rather than having to repeat it in each derived class, then in contracts don't really work. For that, it makes more sense to use NVI so that the in contract is on the public base class or interface function, and the protected, derived functions that the public function calls then don't actually have any in contracts. Either way, I don't think that many would argue that the &&ing of the out contracts is problematic. Regardless, the rules that we have come from languages that are far more into contract rules than we are. So, it's not like we made them up. - Jonathan M Davis
Nov 15 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/15/17 10:39 AM, Jonathan M Davis wrote:
 
 Well, the rules do make sense when you consider that it can make sense for a
 function to allow arguments that the base class didn't. The in contract is
 essentially saying what the function can validly operate on, and there's no
 reason why a derived class couldn't work with more values than the base
 class could. So, the loosening makes sense when you think about it. It just
 means that if you're trying to have a contract that you put in one place and
 then gets reused rather than having to repeat it in each derived class, then
 in contracts don't really work. For that, it makes more sense to use NVI so
 that the in contract is on the public base class or interface function, and
 the protected, derived functions that the public function calls then don't
 actually have any in contracts.
Loosening does make sense, and I'm not opposed to it. But what doesn't make a lot of sense is the inability to declare the inheritance of the current in-contract situation. If you have a complex contract, then having to repeat it on every class seems unnecessarily verbose. It's very easy to just say `in {}` if you now can accept all possible parameters. The opposite is not as easy. -Steve
Nov 15 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/15/17 11:19 AM, Steven Schveighoffer wrote:
 But what doesn't make a lot of sense is the inability to declare the 
 inheritance of the current in-contract situation. If you have a complex 
 contract, then having to repeat it on every class seems unnecessarily 
 verbose. It's very easy to just say `in {}` if you now can accept all 
 possible parameters. The opposite is not as easy.
I would bet money that at least 50% of the time contract inheritance is used, people use base contracts to mean THE contract, and don't put contracts on derived functions, and they don't even realize it. -Steve
Nov 15 2017
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 15, 2017 11:21:16 Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On 11/15/17 11:19 AM, Steven Schveighoffer wrote:
 But what doesn't make a lot of sense is the inability to declare the
 inheritance of the current in-contract situation. If you have a complex
 contract, then having to repeat it on every class seems unnecessarily
 verbose. It's very easy to just say `in {}` if you now can accept all
 possible parameters. The opposite is not as easy.
I would bet money that at least 50% of the time contract inheritance is used, people use base contracts to mean THE contract, and don't put contracts on derived functions, and they don't even realize it.
I expect so. When the issue comes up, I frequently have to either think it through or look up the rules if I want to remember what they are. But honestly, I've given up on using contracts in general, and I rarely use classes, so it's not something that I usually think about. - Jonathan M Davis
Nov 15 2017
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 15, 2017 14:43:33 Dr. Assembly via Digitalmars-d-
learn wrote:
 I'm learning to use interface with contracts. In below code, in
 isn't being "called". Can someone point out why? what am I doing
 wrong?

 void main() {
   C c = new C();
   writeln(c.foo(1));
 }

 interface I
 {
   int foo(int i)
       in { assert(i > 2); }
       out (result) { assert(result != 0); }

   void baa();
 }

 class C : I
 {
   int foo(int i) {
       return i * 2;
   }

   void baa() {
       writeln("Hello!");
   }
 }
Okay. When inheritance gets involved, contracts get a bit special. If you had a free function or a struct's member function, things would be simple enough. The in contract would be run before the function's bode was run, and the out contract would be run afterwards. If either failed, an AssertError would be thrown. However, it works a bit differently with classes. To quote the documentation ( section 23.3: https://dlang.org/spec/contracts.html ): =========== 1. If a function in a derived class overrides a function in its super class, then only one of the in contracts of the function and its base functions must be satisfied. Overriding functions then becomes a process of loosening the in contracts. 2. A function without an in contract means that any values of the function parameters are allowed. This implies that if any function in an inheritance hierarchy has no in contract, then in contracts on functions overriding it have no useful effect. 3. Conversely, all of the out contracts need to be satisfied, so overriding functions becomes a processes of tightening the out contracts. =========== So, effectively, in contracts are all ||ed and out contracts are all &&ed, and the lack of an in contract means that that contract passes. So, if you don't have an in contract, or if you have an empty one, then all base class (or interface) contracts are pointless for that derived function. So, in contracts only really get inherited in the sense that it gives you another contract that could pass. It really doesn't provide a way to restrict derived functions. So, it's pointless to put an in contract on an interface's function unless you're trying to ensure that nothing in derived contracts is any stricter than that contract, which in practice likely means that it's pointless to put an in contract on an interface function. However, out contracts do restrict derived functions, since those are effectively &&ed together. So, it can make sense to put those are interfaces. - Jonathan M Davis
Nov 15 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/15/17 10:14 AM, Jonathan M Davis wrote:

  So, it's pointless to put an in contract on an
 interface's function unless you're trying to ensure that nothing in derived
 contracts is any stricter than that contract, which in practice likely means
 that it's pointless to put an in contract on an interface function.
 
Just tested this, and actually, if you put no contract on an interface function, it negates any contracts on the implementing functions. So there definitely is a use for the in contract on an interface -- if you plan on defining contracts on implementing types. I'm curious why the absence of an in contract disables all other in contracts. I would have expected it to simply have no effect. -Steve
Nov 15 2017
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 15, 2017 10:33:50 Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On 11/15/17 10:14 AM, Jonathan M Davis wrote:
  So, it's pointless to put an in contract on an

 interface's function unless you're trying to ensure that nothing in
 derived contracts is any stricter than that contract, which in practice
 likely means that it's pointless to put an in contract on an interface
 function.
Just tested this, and actually, if you put no contract on an interface function, it negates any contracts on the implementing functions. So there definitely is a use for the in contract on an interface -- if you plan on defining contracts on implementing types. I'm curious why the absence of an in contract disables all other in contracts. I would have expected it to simply have no effect.
The issue is that if _any_ in contract passes, then they all pass, and the lack of an in contract is the same as a contract that always passes. So, I guess that if you're trying to put contracts on your derived classes, the in contract on the interface function isn't really pointless, but it really doesn't have the desired effect. Basically, once you have any class or interface in the inheritance chain, you have to put in contracts on _all_ of them if you want them to have any effect - and whichever one is the loosest is the one that's going to really matter. All in all, I expect that in most cases, using NVI with an in contract on the public function would be much closer to what folks would be looking for. The way that in contracts work with inheritance makes sense in principle, but it does tend to make them kind of useless in practice. - Jonathan M Davis
Nov 15 2017