November 03, 2017
On Thursday, 2 November 2017 at 18:40:26 UTC, bauss wrote:
> I disagree with that, because it would make the language very verbose.

Personally, I think function headers are starting to become to verbose.

I don't believe removing the separate scaffolding that accompanies contracts, so that you incorporate contracts directly into the scaffolding of a function header is a good design choice.

There was an inital design choice to put in that scaffolding for contracts, and presumably it was done so for a reason. I didn't see that discussed in the DIP.

November 02, 2017
On Friday, November 03, 2017 02:08:43 codephantom via Digitalmars-d wrote:
> On Thursday, 2 November 2017 at 18:40:26 UTC, bauss wrote:
> > I disagree with that, because it would make the language very verbose.
>
> Personally, I think function headers are starting to become to verbose.
>
> I don't believe removing the separate scaffolding that accompanies contracts, so that you incorporate contracts directly into the scaffolding of a function header is a good design choice.
>
> There was an inital design choice to put in that scaffolding for contracts, and presumably it was done so for a reason. I didn't see that discussed in the DIP.

They're designed so that you can run more or less arbitrary code, and for that having normal blocks like in, out, and invariant currently do makes a lot of sense. The issue is that if you just need something short - especially if you're just asserting some conditions and not running any other code to set up the assertions - then they get awfully verbose for what they're doing. So, having a terser syntax for simpler contracts is desirable.

Personally, I hate how verbose they are, but my solution is just not to use them. And IMHO, the only place that they add real value is in classes, where their success or failure can be &&ed or ||ed based on how that should work with inheritance. For struct member functions or free functions, where no inheritance is involved, they add considerably less value.

in contracts for functions that aren't members of classes can just be done at the beginning of the function, and you don't lose anything. If some aspect of the contracts were checked at compile time, or if whether contracts were run or not depended on how the caller was compiled rather than the callee, then having an explicit in contract could be useful. But as it stands, they really don't add anything over simply asserting at the beginning of the function (except in the case where inheritance is involved).

out contracts IMHO are almost universally useless for pretty much the same reason that unittest blocks that are compiled into templates are almost universally useless: they have to be so generic that in most cases, you can't do much with them. It almost always works far better to test the result of functions by using unit tests to test that known inputs give the correct results. Pretty much the only case where out contracts work well is when you have a very specific, testable condition that all results must have and which does not depend on the input, and most functions simply don't work that way.

And while invariants are of some value for classes, IMHO, they should be avoided like the plague in structs. The problem is that _every_ public function (including opAssign) has the invariant called before and after. That means if you ever try and do anything with void initialization for performance, you're screwed if you have an invariant, because whatever garbage is in the member variables will be tested as soon as you try to assign a valid value to the object. If it just tested _after_ opAssign, then that would be fine, but it tests before as well. And it may actually be that if emplace is involved (e.g. with an allocator) that initializing a class with an invariant could have the same problem. So, much as I think that invariants could add real value (certainly, they're far less verbose than manually adding those same checks to ever public member function), I never use them.

So, while I really don't like how verbose contracts in D are, and I wouldn't mine a more concise syntax, I also find it hard to care, because I'm not going to use them either way.

- Jonathan M Davis

November 03, 2017
On Friday, 3 November 2017 at 02:32:41 UTC, Jonathan M Davis wrote:
> Pretty much the only case where out contracts work well is when you have a very specific, testable condition that all results must have and which does not depend on the input, and most functions simply don't work that way.
> - Jonathan M Davis

Can you give an example of a function for which this doesn't work?

November 03, 2017
On Friday, 3 November 2017 at 02:32:41 UTC, Jonathan M Davis wrote:
> Personally, I hate how verbose they are, but my solution is just not to use them. And IMHO, the only place that they add real value is in classes, where their success or failure can be &&ed or ||ed based on how that should work with inheritance. For struct member functions or free functions, where no inheritance is involved, they add considerably less value.

One advantage is documentation of expected pre-conditions. With the proposed block-less syntax, people would presumably use them more. Then there would be a bigger motivation for some compilers to insert the contracts at the call site, and use this information for optimization on the caller side, or to point out logical inconsistencies in the calling code (e.g. detecting invalid null arguments).
November 04, 2017
On Friday, November 03, 2017 12:34:22 Mark via Digitalmars-d wrote:
> On Friday, 3 November 2017 at 02:32:41 UTC, Jonathan M Davis
>
> wrote:
> > Pretty much the only case where out contracts work well is when
> > you have a very specific, testable condition that all results
> > must have and which does not depend on the input, and most
> > functions simply don't work that way.
> > - Jonathan M Davis
>
> Can you give an example of a function for which this doesn't work?

Um, most functions? Heck, take a really simply one like sqrt. All you have to check in the out contract is the return value. You have no idea what was passed in. So, how would you write an out contract verifying that you got the correct number? If you also had access to the input, then you could do the reverse operation by squaring the result to see if it matched the input (assuming of course that floating point rounding errors don't make that not work), but you don't have access to the input.

And even if you did have access to the input, some functions consume their input without any way to save it (e.g. an input range that isn't a forward range), and not all operations are reversible - e.g. if you have a hash function, you can't get the original value back from it to check against what was passed in. So, for a lot of stuff, even if you had the original input, you would have to essentially implement the function a second time in the out contract to verify the result, and who's to say that the implementation in the out contract isn't just as buggy as the one in the main function (assuming that it's even different at all)? And that's still assuming that you had access to the input parameters in the out contract, which you don't.

In order to have an out contract be of any use, it needs to be able to verify something about the state of the result without having any clue what the input was. You essentially need to be able to pass the result to a function that says whether an arbitrary result is valid or not. Something like sort can do that, because you know that the result is supposed to be sorted, and that's a testable condition (albeit a condition that's not very cheap to test), but for most functions there simply doesn't exist a test for knowing whether the result is correct by only looking at the result.

On the other hand, if you know what output a function is supposed to have for a given input, it's trivial to test that with a unit test. The only thing that's worse about that is that if you could somehow test the result in an out contract, then it's possible to test the result for every invocation of that function instead of just testing a specific set of inputs. So, upon occasion, an out contract may be useful, but it rarely is, and even if it is, if your unit tests are good, they'll likely catch all of the problems that the out contract would catch, and the out contract would slow down every non-release build, so if the unit tests are enough to catch the bugs, then having the out contract just needlessly slows down your program (which may or may not matter, depending on what you do with non-release builds, but it can matter).

So, all in all, I think that unit tests solve the problem that out contracts are trying to solve and do it in a way that works in general, whereas out contracts only work in specific cases. So, I don't see much point in using them.

- Jonathan M Davis

November 04, 2017
On Saturday, 4 November 2017 at 06:08:22 UTC, Jonathan M Davis wrote:
> Heck, take a really simply one like sqrt. All you have to check in the out contract is the return value. You have no idea what was passed in. So, how would you write an out contract verifying that you got the correct number? If you also had access to the input, then you could do the reverse operation by squaring the result to see if it matched the input (assuming of course that floating point rounding errors don't make that not work), but you don't have access to the input.

I don't think I've ever written an out contract, so I am inclined to agree with you. However, there is a sqrt example for integers in the official docs, it does access its input:

https://dlang.org/spec/contracts.html#pre_post_contracts

long square_root(long x)
in
{
    assert(x >= 0);
}
out (result)
{
    assert((result * result) <= x && (result+1) * (result+1) > x);
}
November 04, 2017
On Saturday, November 04, 2017 13:02:45 Nick Treleaven via Digitalmars-d wrote:
> On Saturday, 4 November 2017 at 06:08:22 UTC, Jonathan M Davis
>
> wrote:
> > Heck, take a really simply one like sqrt. All you have to check in the out contract is the return value. You have no idea what was passed in. So, how would you write an out contract verifying that you got the correct number? If you also had access to the input, then you could do the reverse operation by squaring the result to see if it matched the input (assuming of course that floating point rounding errors don't make that not work), but you don't have access to the input.
>
> I don't think I've ever written an out contract, so I am inclined to agree with you. However, there is a sqrt example for integers in the official docs, it does access its input:
>
> https://dlang.org/spec/contracts.html#pre_post_contracts
>
> long square_root(long x)
> in
> {
>      assert(x >= 0);
> }
> out (result)
> {
>      assert((result * result) <= x && (result+1) * (result+1) > x);
> }

I was sure that you couldn't do that, but apparently, I was wrong. However, it does rely on the parameter not having been mutated. e.g.

void main()
{
    foo(42);
}

void foo(int x)
out
{
    import std.stdio;
    writeln(x);
}
body
{
    x = 7;
}

prints 7.

Being able to access the input of a function that doesn't mutate its input does increase the usefulness of out contracts, but there are still plenty of functions where you can't determine whether the output is correct just by looking at the input without reimplementing the function in the out contract to make sure that the results match (e.g. as I pointed out before, a hash function isn't reversible, which means that you can't just verify the hash for arbitrary input).

I'm very much of the opinion that proper unit tests pretty much eliminate the need for out contracts.

- Jonathan M Davis

November 04, 2017
On Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:
> I'm very much of the opinion that proper unit tests pretty much eliminate the need for out contracts.

I think that sqrt example is just bad. Out contracts shouldn't be testing specific values, but rather ranges or nullness or other such things that arguably should be part of the return type, but just don't fit in the type system for whatever reason.

Consider this:

class A {
        A clone()
        out(result) { assert(result !is null); }
        body {
                return new A();
        }
}
class B : A {
        override B clone() {
                return new B();
        }
}

void main() {
        A a = new B();
        A c = a.clone();
}


The `clone` method defined in the base class arguably out to return a NotNull!A, encoding that contract in the type, but we can't really do that with D without breaking the inheritance - where B is statically defined to return B too, since NotNull!B isn't covariant to NotNull!A the way B is covariant to A. (At least not with any techniques I can think of at this time.)

So you can't encode it in the return type... but you can encode it in the out contract for a runtime check.

It also clearly documents that the subclasses must not return null (and can add their own restrictions to further subclasses). One problem I have with some of D's contracts right now is that they are convoluted, ugly code that make for passable internal unit tests, you can't reasonably display as documentation as part of the interface.

But regardless, I say you should not think of out as tests. Think of it as type system extensions.
November 04, 2017
On Saturday, 4 November 2017 at 14:12:08 UTC, Adam D. Ruppe wrote:
> On Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:
>> I'm very much of the opinion that proper unit tests pretty much eliminate the need for out contracts.
>
> I think that sqrt example is just bad. Out contracts shouldn't be testing specific values, but rather ranges or nullness or other such things that arguably should be part of the return type, but just don't fit in the type system for whatever reason.

No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.


November 04, 2017
On Saturday, November 04, 2017 15:27:39 Ola Fosheim Grøstad via Digitalmars- d wrote:
> On Saturday, 4 November 2017 at 14:12:08 UTC, Adam D. Ruppe wrote:
> > On Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis
> >
> > wrote:
> >> I'm very much of the opinion that proper unit tests pretty much eliminate the need for out contracts.
> >
> > I think that sqrt example is just bad. Out contracts shouldn't be testing specific values, but rather ranges or nullness or other such things that arguably should be part of the return type, but just don't fit in the type system for whatever reason.
>
> No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.

In principle, that would be nice, but in practice, it's not really feasible. In the general case, there's no way to save the state of the parameter at the beginning of the function call (you could with some types, but for many types, you couldn't). IIRC, it's been discussed quite a bit in the past, and there are just too many problems with it.

- Jonathan M Davis