digitalmars.D - Improve "Improve Contract Syntax" DIP 1009
- Andrei Alexandrescu (25/25) Nov 01 2017 We're having difficulty reviewing
- Meta (7/35) Nov 01 2017 This actually makes the DIP slightly longer but hopefully makes
- Andrei Alexandrescu (3/49) Nov 06 2017 Thanks! We're still looking for sizeable improvements. Any volunteers,
- codephantom (18/20) Nov 01 2017 Well, I'm struggling with the 'claims' on which the DIP mounts
- H. S. Teoh (34/50) Nov 01 2017 Sigh. At the time of the review, I *did* mention that it needs to be
- codephantom (22/25) Nov 02 2017 Yeah.. I had a brief look over some of those discussions before
- Patrick Schluter (17/28) Nov 02 2017 if() in template constraints are similar, so it is nothing
- codephantom (5/9) Nov 02 2017 you know the old saying... what happens in vegas stays in vegas...
- bauss (3/14) Nov 02 2017 I disagree with that, because it would make the language very
- codephantom (9/11) Nov 02 2017 Personally, I think function headers are starting to become to
- Jonathan M Davis (47/58) Nov 02 2017 They're designed so that you can run more or less arbitrary code, and fo...
- Mark (3/8) Nov 03 2017 Can you give an example of a function for which this doesn't work?
- Jonathan M Davis (44/52) Nov 03 2017 Um, most functions? Heck, take a really simply one like sqrt. All you ha...
- Nick Treleaven (15/23) Nov 04 2017 I don't think I've ever written an out contract, so I am inclined
- Jonathan M Davis (29/52) Nov 04 2017 I was sure that you couldn't do that, but apparently, I was wrong. Howev...
- Adam D. Ruppe (39/41) Nov 04 2017 I think that sqrt example is just bad. Out contracts shouldn't be
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (3/11) Nov 04 2017 No, Jonathan is correct. The postcondition should be able to
- Jonathan M Davis (8/21) Nov 04 2017 In principle, that would be nice, but in practice, it's not really feasi...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (12/18) Nov 04 2017 It is obviously possible for a language that has been designed
- Mark (6/31) Nov 04 2017 Yeah, I can see some of the problems that would arise from that.
- MrSmith (14/21) Nov 05 2017 What about making definitions in "in" block visible in "out"
- Jonathan M Davis (31/53) Nov 05 2017 That would help, and I think that that it was discussed previously, but ...
- Timon Gehr (8/18) Nov 06 2017 The point of the in contract is to establish that it is the caller, and
- H. S. Teoh (22/45) Nov 04 2017 [...]
- Adam D. Ruppe (5/7) Nov 04 2017 Yes, they should be able to access values, but D's limitations on
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (12/19) Nov 04 2017 But then they do a job that subtyping does better…? So why do
- Timon Gehr (10/20) Nov 04 2017 It is indeed bad, because it is buggy.
- Jonathan M Davis (11/52) Nov 05 2017 It's an interesting idea, but I use classes so rarely in D that I wouldn...
- Mark (4/11) Nov 04 2017 True. I had (strongly) pure functions in my mind and I didn't
- Nick Treleaven (9/15) Nov 03 2017 One advantage is documentation of expected pre-conditions. With
- user1234 (16/19) Nov 02 2017 There's this one:
- user1234 (2/3) Nov 02 2017 replace function by void.
- Nick Treleaven (16/22) Nov 06 2017 I've found a use for it - breakable blocks. (It's a bit neater
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
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? AndreiThis 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
On 11/1/17 11:11 PM, Meta wrote:On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei Alexandrescu wrote:Thanks! We're still looking for sizeable improvements. Any volunteers, please holler. -- AndreiWe'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? AndreiThis 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 06 2017
On Wednesday, 1 November 2017 at 22:04:10 UTC, Andrei Alexandrescu wrote:There are some unsupported claims .... AndreiWell, 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
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: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.There are some unsupported claims ....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
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
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
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 definitionyou 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
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:I disagree with that, because it would make the language very verbose.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 definitionyou 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
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
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: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 DavisI 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
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 DavisCan you give an example of a function for which this doesn't work?
Nov 03 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: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 DavisPretty 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 DavisCan you give an example of a function for which this doesn't work?
Nov 03 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); }
Nov 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: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 DavisHeck, 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
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
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:No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.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.
Nov 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: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 DavisOn Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.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.
Nov 04 2017
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
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: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. :(On Saturday, 4 November 2017 at 14:12:08 UTC, Adam D. Ruppe 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 DavisOn Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.[...]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.
Nov 04 2017
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 DavisWhat 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
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: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 DavisIn 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 DavisWhat 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
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 contractThen 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
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:[...] 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.On Saturday, 4 November 2017 at 14:12:08 UTC, Adam D. Ruppe 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.On Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:No, Jonathan is correct. The postcondition should be able to access values as they were stated in the precondition.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.
Nov 04 2017
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
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: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).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
On 04.11.2017 10:12, Adam D. Ruppe wrote:On Saturday, 4 November 2017 at 13:59:39 UTC, Jonathan M Davis wrote:It is indeed bad, because it is buggy. (The out contract may overflow.)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,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
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: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 DavisI'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 05 2017
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 DavisTrue. 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
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
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
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
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