www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Improve "Improve Contract Syntax" DIP 1009

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
We're having difficulty reviewing 
https://github.com/dlang/DIPs/blob/master/DIPs/DIP1009.md. The value is 
there, but the informal and sometimes flowery prose affects the document 
negatively. There are some unsupported claims and detailed description 
is sketchy. We need a careful pass that replaces the unclear or 
imprecise statements with clear, straightforward scientific claims.

Can anyone help with this? For example, the first paragraph:

"D has already made a significant commitment to the theory of Contract 
Programming, by means of its existing in, out, and invariant constructs. 
But limitations remain to their usability, both in their syntax and in 
their implementation. This DIP addresses only the syntax aspect of those 
limitations, proposing a syntax which makes in, out, and invariant 
contracts much easier to read and write."

could be:

"The D language supports Contract Programming by means of its in, out, 
and invariant constructs. Their current syntactic form is unnecessarily 
verbose. This DIP proposes improvements to the contract syntax that 
makes them easier to read and write."

The change:

* eliminates the entire "implementation sucks" allegation which seems 
taken straight from a forum flamewar;

* replaces adjective-laden language with simple and precise statements;

* provides a brief factual overview of what follows.

Who wants to help?


Andrei
Nov 01 2017
next sibling parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei 
Alexandrescu wrote:
 We're having difficulty reviewing 
 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1009.md. The 
 value is there, but the informal and sometimes flowery prose 
 affects the document negatively. There are some unsupported 
 claims and detailed description is sketchy. We need a careful 
 pass that replaces the unclear or imprecise statements with 
 clear, straightforward scientific claims.

 Can anyone help with this? For example, the first paragraph:

 "D has already made a significant commitment to the theory of 
 Contract Programming, by means of its existing in, out, and 
 invariant constructs. But limitations remain to their 
 usability, both in their syntax and in their implementation. 
 This DIP addresses only the syntax aspect of those limitations, 
 proposing a syntax which makes in, out, and invariant contracts 
 much easier to read and write."

 could be:

 "The D language supports Contract Programming by means of its 
 in, out, and invariant constructs. Their current syntactic form 
 is unnecessarily verbose. This DIP proposes improvements to the 
 contract syntax that makes them easier to read and write."

 The change:

 * eliminates the entire "implementation sucks" allegation which 
 seems taken straight from a forum flamewar;

 * replaces adjective-laden language with simple and precise 
 statements;

 * provides a brief factual overview of what follows.

 Who wants to help?


 Andrei
This actually makes the DIP slightly longer but hopefully makes it more clear. https://github.com/dlang/DIPs/pull/95 I'm heading off to bed so I won't be able to respond right away to suggested changes.
Nov 01 2017
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/1/17 11:11 PM, Meta wrote:
 On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei Alexandrescu wrote:
 We're having difficulty reviewing 
 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1009.md. The value 
 is there, but the informal and sometimes flowery prose affects the 
 document negatively. There are some unsupported claims and detailed 
 description is sketchy. We need a careful pass that replaces the 
 unclear or imprecise statements with clear, straightforward scientific 
 claims.

 Can anyone help with this? For example, the first paragraph:

 "D has already made a significant commitment to the theory of Contract 
 Programming, by means of its existing in, out, and invariant 
 constructs. But limitations remain to their usability, both in their 
 syntax and in their implementation. This DIP addresses only the syntax 
 aspect of those limitations, proposing a syntax which makes in, out, 
 and invariant contracts much easier to read and write."

 could be:

 "The D language supports Contract Programming by means of its in, out, 
 and invariant constructs. Their current syntactic form is 
 unnecessarily verbose. This DIP proposes improvements to the contract 
 syntax that makes them easier to read and write."

 The change:

 * eliminates the entire "implementation sucks" allegation which seems 
 taken straight from a forum flamewar;

 * replaces adjective-laden language with simple and precise statements;

 * provides a brief factual overview of what follows.

 Who wants to help?


 Andrei
This actually makes the DIP slightly longer but hopefully makes it more clear. https://github.com/dlang/DIPs/pull/95 I'm heading off to bed so I won't be able to respond right away to suggested changes.
Thanks! We're still looking for sizeable improvements. Any volunteers, please holler. -- Andrei
Nov 06 2017
prev sibling parent reply codephantom <me noyb.com> writes:
On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei 
Alexandrescu wrote:
 There are some unsupported claims ....
 Andrei
Well, I'm struggling with the 'claims' on which the DIP mounts its argument. (1) That the current syntax for contract programming limits its usability. (2) That the more concise syntax being proposed, is both easier to read and write, and will therefore increase the usage of contract programming. The problem with the DIP, as I see it, is: The evidence for claim (1) is presumably points 1,2,3 in the Rationale. However, there is no convincing evidence to suggest there really is a connection between those points and that claim. The evidence for claim (2) is?? Claims asserted as true without justification are just assumptions. DIP authours would do well too study the Toulmin method of argumentation.
Nov 01 2017
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Nov 02, 2017 at 03:40:17AM +0000, codephantom via Digitalmars-d wrote:
 On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei Alexandrescu wrote:
 There are some unsupported claims ....
Sigh. At the time of the review, I *did* mention that it needs to be clearer in its presentation and especially should include the rationale for the syntax as proposed. However, the response I got was "it's not necessary, because it already references the previous versions of the DIP and therefore ought to be clear already". Well, so much for that.
 Well, I'm struggling with the 'claims' on which the DIP mounts its
 argument.
 
 (1) That the current syntax for contract programming limits its
 usability.
This could have been better worded. It doesn't technically *limit* anything, but is more of a deterrent to using contracts because doing so with the current syntax brings quite a bit of verbosity to the code. A lot of the arguments and reasonings happened on the forum review thread, which *should* have been included in summary form in the final version of the DIP. But it didn't. *sigh*
 (2) That the more concise syntax being proposed, is both easier to
 read and write, and will therefore increase the usage of contract
 programming.
[...]
 The evidence for claim (2) is??
 
 Claims asserted as true without justification are just assumptions.
 
 DIP authours would do well too study the Toulmin method of
 argumentation.
This is uncalled for. This DIP did go through extensive review and discussion on the forum review threads, and actual code examples and syntax alternatives were put forth and argued for/against. There was ample evidence in the form of testimonials from various D users about the verbosity of the current cumbersome syntax, and this DIP certainly was not the first time this issue was brought up. Calling it mere "assumptions" is a bit heavy-handed, given the actual precedents. It's not entirely your fault, though, since you probably were not here at the time, and wouldn't have been aware of said discussions. This is why the DIP should have included all of those discussions (in summary form, of course), which is its only real fault here. I did raise this issue, but it wasn't heeded to. The argument at the time was that summarizing the discussions in the forum threads was unnecessary because, supposedly, Walter and Andrei would have already seen the discussions, and it was thought better to minimize the workload on their part by shortening the DIP. Well, that didn't quite go so well, did it? Now somebody will have to go through those review threads, summarize them, and add them to the DIP. T -- ASCII stupid question, getty stupid ANSI.
Nov 01 2017
parent reply codephantom <me noyb.com> writes:
On Thursday, 2 November 2017 at 04:26:20 UTC, H. S. Teoh wrote:
 It's not entirely your fault, though, since you probably were 
 not here at the time, and wouldn't have been aware of said 
 discussions.
Yeah.. I had a brief look over some of those discussions before posting my comment above. But even there I could not see a strong case being made. This would be nice..convenient..and save a few key strokes is one thing... ..but we must go write a DIP because 'we need a change' is another all together. The 'case of the stolen body' ;-) ... seems to have been made solved though.. and 'do' seemed like a reasonable replacement. but now we want free floating statements in D? like: ---------------------- in(a > 0) in(b >= 0, "b cannot be negative!") out(r; r > 0, "return must be positive") out(; a != 0) -------------------- Now, I'm new to D (I only discovered it a month or so ago)...but do free floating statements already exist in the language? If I saw that when I came to D a month or so ago, I'd would probably have moved on to something else...
Nov 02 2017
next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 2 November 2017 at 08:58:03 UTC, codephantom wrote:

 but now we want free floating statements in D?

 like:
 ----------------------
 in(a > 0)
 in(b >= 0, "b cannot be negative!")
 out(r; r > 0, "return must be positive")
 out(; a != 0)
 --------------------

 Now, I'm new to D (I only discovered it a month or so 
 ago)...but do free floating statements already exist in the 
 language?
if() in template constraints are similar, so it is nothing shockingly out of character. They are not free floating either as they are part of the function definition int foo(int a) in(a >= 0) do { ...whatever } vs int foo(int a) in { assert(a >= 0); } do { ...whatever }
Nov 02 2017
parent reply codephantom <me noyb.com> writes:
On Thursday, 2 November 2017 at 09:18:51 UTC, Patrick Schluter 
wrote:
 if() in template constraints are similar, so it is nothing 
 shockingly out of character.
 They are not free floating either as they are part of the 
 function definition
you know the old saying... what happens in vegas stays in vegas... well... the same should be said for template programming. what happens in templates, should stay in templates.
Nov 02 2017
parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 2 November 2017 at 10:47:09 UTC, codephantom wrote:
 On Thursday, 2 November 2017 at 09:18:51 UTC, Patrick Schluter 
 wrote:
 if() in template constraints are similar, so it is nothing 
 shockingly out of character.
 They are not free floating either as they are part of the 
 function definition
you know the old saying... what happens in vegas stays in vegas... well... the same should be said for template programming. what happens in templates, should stay in templates.
I disagree with that, because it would make the language very verbose.
Nov 02 2017
parent reply codephantom <me noyb.com> writes:
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.
Nov 02 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 02 2017
next sibling parent reply Mark <smarksc gmail.com> writes:
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?
Nov 03 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 03 2017
next sibling parent reply Nick Treleaven <nick geany.org> writes:
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); }
Nov 04 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 04 2017
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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.
Nov 04 2017
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Nov 04 2017
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 04 2017
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Saturday, 4 November 2017 at 15:38:42 UTC, Jonathan M Davis 
wrote:
 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.
It is obviously possible for a language that has been designed for contracts. Whether it is practical is a matter of optimization in my view… Anyway, without having such a focus it makes little sense to say that a language supports programming by contracts or pre/post conditions. It is more of a syntactical construct in D. Although class invariants are still useful in debugging. C++ is getting pre/post conditions I think. Not sure if it will be a plain syntactical construct like in D or something more advanced.
Nov 04 2017
prev sibling next sibling parent Mark <smarksc gmail.com> writes:
On Saturday, 4 November 2017 at 15:38:42 UTC, Jonathan M Davis 
wrote:
 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 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
Yeah, I can see some of the problems that would arise from that. I think out contracts can work well for safe strongly pure functions, but IIRC most functions in Phobos aren't strongly pure. Too bad. :(
Nov 04 2017
prev sibling parent reply MrSmith <mrsmith33 yandex.ru> writes:
On Saturday, 4 November 2017 at 15:38:42 UTC, Jonathan M Davis 
wrote:
 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
What about making definitions in "in" block visible in "out" block? void fun(int par) in { int saved = par; } body { par = 7; } out { writeln(saved); }
Nov 05 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, November 05, 2017 16:25:16 MrSmith via Digitalmars-d wrote:
 On Saturday, 4 November 2017 at 15:38:42 UTC, Jonathan M Davis

 wrote:
 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
What about making definitions in "in" block visible in "out" block? void fun(int par) in { int saved = par; } body { par = 7; } out { writeln(saved); }
That would help, and I think that that it was discussed previously, but it would mean that in and out were in the same scope, which would be a breaking change. There may have been other reasons why it would be problematic, but I don't recall. There's certainly nothing stopping the creation of a DIP on the topic though if someone feels strongly enough about it to put in the time and effort. I'm sure that there are various things that could be done to improve contracts (and DIP 1009 is one such attempt), but I've mostly given up on it. I don't think that they provide enough value in the first place. Aside from issues involving contracts and inheritance, in contracts can just be put in the function body, making them kind of pointless, and for the most part, unit tests test anything that I would consider testing in an out contract - and generally do it better. That combined with the issue with invariant and void initialized objects, and as far as I'm concerned, contracts are pretty pointless, much as they're well-intentioned. I almost never use them. Probably the one thing that would make me change my tune on in contracts would be if it were somehow changed so that whether they were run depended on whether the caller was compiled with assertions enabled rather than whether the function being called was compiled with assertions, since with in contracts, it's really the caller that you're testing, not the function itself. But short of that, I doubt I'll ever do much with them. However, one thing that annoys me enough that I might do something about it at some point is how invariant is broken with void initialized objects, since unlike in and out contracts, doing what invariant does without using the feature is a royal pain. So, at some point, I may create a DIP to deal with that (probably by providing a way to explicitly indicate that you want the invariant skipped at the callsite), but it hasn't been a particularly high priority for me, so I haven't done anything with it. - Jonathan M Davis
Nov 05 2017
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 06.11.2017 03:32, Jonathan M Davis wrote:
 
 I'm sure that there are various things that could be done to improve
 contracts (and DIP 1009 is one such attempt), but I've mostly given up on
 it. I don't think that they provide enough value in the first place. Aside
 from issues involving contracts and inheritance, in contracts can just be
 put in the function body,
The point of the in contract is to establish that it is the caller, and not the library author, who is responsible for ensuring it is satisfied. It is slightly more obvious why this is crucial if your language has tooling for verifying assertions.
 making them kind of pointless, and for the most
 part, unit tests test anything that I would consider testing in an out
 contract
Then you are doing it wrong.
 - and generally do it better.
Unit tests alone do not test your code better than both unit tests and contracts.
Nov 06 2017
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Nov 04, 2017 at 09:38:42AM -0600, Jonathan M Davis via Digitalmars-d
wrote:
 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.
[...] This isn't as bad as it sounds. If you really cared for out-contracts being able to access the original state of the parameters, just make the parameters const. In the cases where you can't make them const, out-contracts are probably impractical anyway -- independently of how D implements them, if your function is going to mutate parameters then the only way the out-contract would work is if a copy of the parameters were made. But if you're already going to do that, might as well just make the parameters const and do the copy inside the function body. But as Adam said, out-contracts aren't really intended for semantic enforcement, but they are more like extensions of the type system. E.g., asserting that a returned pointer is never null, or that a returned value is never negative, or stays within some range of values, etc.. Basically, stuff the caller can assume about the return value as far as the set of possible values is concerned. For most non-trivial functions, semantic enforcement requires you to basically reimplement the function body, which is kinda pointless. If you want semantic enforcement, that's what unittests are for. T -- Caffeine underflow. Brain dumped.
Nov 04 2017
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 4 November 2017 at 15:27:39 UTC, Ola Fosheim Grøstad 
wrote:
 No, Jonathan is correct. The postcondition should be able to 
 access values as they were stated in the precondition.
Yes, they should be able to access values, but D's limitations on this doesn't make them useless or replaced by unittests. They do a different job than unittests.
Nov 04 2017
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Saturday, 4 November 2017 at 16:57:50 UTC, Adam D. Ruppe wrote:
 On Saturday, 4 November 2017 at 15:27:39 UTC, Ola Fosheim 
 Grøstad wrote:
 No, Jonathan is correct. The postcondition should be able to 
 access values as they were stated in the precondition.
Yes, they should be able to access values, but D's limitations on this doesn't make them useless or replaced by unittests. They do a different job than unittests.
But then they do a job that subtyping does better…? So why do this with "contracts"? Anyhow, I think the ideal for contracts in terms of debugging is to express postconditions in terms of public interface rather than internal representation, e.g. for a stack push: oldthis.size() + 1 == newthis.size() // or length() or whatever the interface provides Then hammer the interface in unit tests rather than writing the explicit test in the unit test itself. (e.g. the unit test does not have to know the conditions, just explore a wide range of configurations).
Nov 04 2017
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04.11.2017 10:12, 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.
It is indeed bad, because it is buggy. (The out contract may overflow.)
 Out contracts shouldn't be 
 testing specific values,
It's not testing specific values. It is testing for input-dependent properties of the output (which in this case happen to pinpoint the output exactly for each input).
 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.
I don't see how that is in contrast to the given example. Clearly, the conditions that it is testing for should have been represented in the type system. Then the example would not compile. Also, one would not be able to complain about runtime overhead.
Nov 04 2017
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, November 04, 2017 14:12:08 Adam D. Ruppe via Digitalmars-d 
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. 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.
It's an interesting idea, but I use classes so rarely in D that I wouldn't hit this use case often enough to even think about it. In fact, at the moment, I don't think that I've _ever_ written a clone function in D, and I probably wouldn't even bother with null checks (in general, not just with clone functions), since the program will just segfault on me if I screw that up, so it's not like it won't be caught, and with the way I write code, I almost never have problems with null objects except in cases where I forgot to initialize something, and that gets found _really_ fast in testing. - Jonathan M Davis
Nov 05 2017
prev sibling parent Mark <smarksc gmail.com> writes:
On Saturday, 4 November 2017 at 06:08:22 UTC, Jonathan M Davis 
wrote:
 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)
 
 [...]
 
 - Jonathan M Davis
True. I had (strongly) pure functions in my mind and I didn't realize that things don't work as smooth in the impure case.
Nov 04 2017
prev sibling parent Nick Treleaven <nick geany.org> writes:
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).
Nov 03 2017
prev sibling parent reply user1234 <user1234 12.ie> writes:
On Thursday, 2 November 2017 at 08:58:03 UTC, codephantom wrote:
 Now, I'm new to D (I only discovered it a month or so 
 ago)...but do free floating statements already exist in the 
 language?
There's this one: void main() { int i; switch (i) default: break; } you have 3 non-ambiguous and contiguous statements without a block: a switch, a default case (the "free-floating" one) and a break. Now why is this allowed is another story ;) The whole combination makes sense just like : function ... in ... out ... do {} would.
Nov 02 2017
next sibling parent user1234 <user1234 12.ie> writes:
On Thursday, 2 November 2017 at 20:37:11 UTC, user1234 wrote:
 On Thursday, 2 November 2017 at 08:58:03 UTC, codephantom wrote:
replace function by void.
Nov 02 2017
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 2 November 2017 at 20:37:11 UTC, user1234 wrote:
     switch (i)
         default: break;
 }

 you have 3 non-ambiguous and contiguous statements without a 
 block: a switch, a default case (the "free-floating" one) and a 
 break. Now why is this allowed is another story ;)
I've found a use for it - breakable blocks. (It's a bit neater than putting the label inside the block): switch (1) default: { auto x = foo(); if (x == bar) break; auto y = x.baz(); if (!y) break; y.writeln; } This avoids indentation from nested if statements instead of breaks. Although this is not ideal syntax, it's better than abusing a loop construct for something that's not a loop. Using a goto makes the code read less naturally as the label is after the block.
Nov 06 2017