digitalmars.D - an old topic (pun intended)
- Davidson Corry (114/114) Oct 26 2011 (continuing a thread from D.learn)
- bearophile (13/17) Oct 26 2011 I think you have written this post in digitalmars.D in a moment when the...
- Davidson Corry (59/68) Oct 26 2011 I had intended for 'exprX' to represent some complex expression, not
- Timon Gehr (17/58) Oct 27 2011 That is what variables are for. I think that should be kept orthogonal.
- Davidson Corry (49/56) Oct 27 2011 I merely intended to show that my proposed syntax could *handle* the
- Timon Gehr (9/65) Oct 27 2011 From the compiler's point of view, both approaches should have
- Davidson Corry (29/34) Oct 27 2011 Agreed. In fact, it occurred to me the other day that we could write
- bearophile (4/17) Oct 27 2011 How do you replace out(result) with scope(out) {} ?
- Davidson Corry (16/30) Oct 29 2011 I am not sure that we need to. D2 has two forms of the 'out()' statement...
- bearophile (18/24) Oct 29 2011 This is not true. Currently 'result' has nothing magic, it's just a vari...
- Davidson Corry (16/24) Oct 30 2011 by mistake), then the current syntax with out(someConstVariableName)
- kennytm (6/45) Oct 28 2011 The contracts are parts of the function's interface, not implementation....
- Davidson Corry (42/77) Oct 29 2011 Just in passing, I think that 'scope(requires)' and 'scope(ensures)' are...
- Davidson Corry (9/9) Oct 27 2011 I want to point out (quick, before someone else does!) that the
- bearophile (5/6) Oct 27 2011 If you don't need to use the method result too, then you probably write ...
- Timon Gehr (2/8) Oct 27 2011 This is possible, but I don't like it too much (see my other post).
(continuing a thread from D.learn) Eiffel supports an 'old' construct in post-conditions. For example, an Add method of a Bag class (generic collection container) might verify that exactly one element was added to the collection, not zero and not more than one: Add(newItem : T) is do -- insert newItem into the Bag ensure exactly_one_added: count = old count + 1 end D2 post-conditions do not provide 'old', and are less useful than they could be for its lack. (Note: D post-conditions appear to be applicable to bare statements, not just functions. I don't see the point of statement post-conditions -- you can do the same thing with 'assert' -- so I focus here on function post-conditions.) It has been suggested that 'unittest' largely obviates the need for 'old'. I disagree with this view. A unit test verifies only the conditions that it explicitly proposes. In contrast, a post-condition will exercise "real-world data" in production-level runs during testing, and is thus likely to trigger on conditions that the unit test never sees. In the D.learn thread, Jonathan M Davis and Ali Çehreli comment JMD> IIRC, old was rejected because it added extra complication for little value. AÇ> Yes, there were technical difficulties in making the stack frame at the call point available to the stack frame at the exit point. Another difficulty is examining the post-condition code for 'old expr' or 'old(expr)' and hoisting those elements out of the middle of an expression for prestate capture. I want to propose a syntax extension that, it seems to me, makes it easier for the compiler to do all of this. In D2, a post-condition is introduced with either 'out(result)' or 'out(...)' [the latter now elided(??) to merely 'out']. That is, 'out' has what appears to be something like an argument list. Let's use it: void foo(T param_T, Q param_Q) out(result, X preX = exprX, Y preY = exprY) { // <== prestate captures added to out "argument" list HERE // various assert()s using the captured prestates } body { // ..actual body of foo() } A lowering of this would look something like void foo(T param_T, Q param_Q) { X __fresh_symbol_for_preX = exprX; Y __fresh_symbol_for_preY = exprY; // ..actual body of foo() assert(__postcondition_code); } That is, the compiler would hoist the prestate variables (and the code that sets their values) out of the "argument" list, and inject them into the function body at its very start, much like a template instantiation. Notes: + "erasing" the actual symbols in the argument list and replacing them with compiler-generated fresh symbols hides those symbols from the function body code (which should not know of them) + but those captured prestate-values exist at the outermost scope of the function body, just as the returned value of the function does. If we can pass the magic symbol 'result' to the post-condition, we should be able to pass the captured prestate + the scope and condition-at-time-of-evaluation for exprX and exprY are exactly what we want: they have access to anything that the function body can access (such as variables in enclosing functions, class members etc.) but NOT to local declarations (which they should not be able to see) because those haven't been declared yet; they are part of the function body code that follows the injected prestate-capture code + the compiler is relieved of the burden of sniffing out "old" sub-elements of expressions in the post-condition body and figuring out exactly what bits of frame and scope need to be preserved to compute them. The onus is on the programmer to declare what prestate he wants to verify, and how to capture it, to whatever level of complexity he wants or needs to employ + exprX etc. can use any syntax element that is legal within the function body (since it is hoisted and injected *into* the function body by the compiler), including things like lambda functions, etc. (In practice, "old" expressions in post-conditions tend to be pretty tame.) + the programmer can name the captured prestate values with any symbols he wishes. This preserves some of the flavor of Eiffel's per-post-condition labels. + capture of prestates is separated from evaluation of post-function conditions *using* those prestates. This may make it more obvious what's being tested and how. + no new 'old' keyword or magic 'old()' function needs to be added to the language, particularly one that is useful only in this isolated corner of the syntax Two weird characteristics of this syntax: - 'result' is evaluated AFTER the function body executes, but prestate-captures are evaluated BEFORE the function body executes - normal argument lists contain only expressions, not declarations of symbols to be assigned from those expressions I think the syntax is clean and flexible enough to forgive those transgressions. An example ('count' and 'color' here are members of an enclosing class, not shown): void change_count_and_color(int countIncrement, bool flipColor) out (result, auto preCount = count, auto previousColor = color) { assert(count == preCount + countIncrement); if (flipColor) { assert(color == ComplementaryColorOf(previousColor)); } assert(isSensible(result)); } body { // ..actual body of the function } ========= Comments, criticisms, jeers and guffaws cheerfully invited. (Also "if you're so smart, there's the source code, YOU implement it!" But then *I'll* guffaw.) -- Davidson
Oct 26 2011
Davidson Corry:(continuing a thread from D.learn)I think you have written this post in digitalmars.D in a moment when the limited brain power is busy with lot of other things :-) I'd like some form of prestate to be implemented in D2. Usually I use manually managed ghost variables (inside a debug{}) to solve this problem, but a more built-in solution is better and more standard.Eiffel supports an 'old' construct in post-conditions.It's often named "prestate".out(result, X preX = exprX, Y preY = exprY) { // <== prestateI'd like something lighter, syntax-wise, where the is no need to state new and old names and types:out(result, exprX, exprY) {And then the compiler lets you access in the postcondition the old variables in some standard way, like: in.exprX, in.exprY Despite this is is probably better looking: old.exprX, old.exprY Here "old" doesn't need to be keyword, it's like the local name of a struct instance. As discussed in D.learn there is no need for deep copying of the old variables. If it's required, then the programmer has to do it manually by herself. This avoids lot of implementation complexities. Bye, bearophile
Oct 26 2011
On 10/26/2011 5:19 PM, bearophile wrote:I had intended for 'exprX' to represent some complex expression, not simply a symbol. For instance, what if the prestate that you want to capture is not merely the value-at-entry of some single datum, but rather a "meta-prestate" computed from multiple source data? For instance, suppose that you were generating hash functions and keys for a cryptographic encoder class, and you had a function figure_of_merit() [NOT a member of the class] that produces some metric for how well the encryption works, based on the results of encrypting each element of an array of sample plain-texts, and analyzing the cryptexts thus produced. The encoder class has data members 'internalKey' (a string used to decode the encrypted text) and 'internalHashDelegate' (a delegate to a hash-generating function which the class uses to encrypt plaintexts). The class also has a function generate_improved_hash_and_key() which potentially modifies either 'internalKey' or 'internalHashDelegate' or both, and you would like to verify that the function actually does improve things, or at least not make them worse: void generate_improved_hash_and_key(string[] plaintexts) out (float preFOM = figure_of_merit(plaintexts, internalKey, internalHashDelegate) ) { assert(preFOM <= figure_of_merit(plaintexts, internalKey, internalHashDelegate); } body { /* ... */ } I cannot see that the expression old.figure_of_merit(plaintexts, internalKey, internalHashDelegate) is an improvement over 'preFOM'. It's also not easy for the compiler to figure out that, in the old.expression, the arguments 'internalKey' and 'internalHashDelegate' are the values of those data at entry-to-function, and somehow need to be cached out to be used in the post-condition comparison, while the very same arguments to the figure_of_merit() function in the body of the post-condition are the values at the END of the function's execution. With the syntax I proposed, the compiler doesn't have to figure out either: the expression in the out(...) argument list gets injected at the very start of the function body, and evaluates based on the conditions BEFORE the function body gets going, while the body of the post-condition is conceptually injected at the very end of the function body [as if in a scope(exit) block] and uses the conditions that hold AFTER the function body has completed execution.out(result, X preX = exprX, Y preY = exprY) { //<== prestateI'd like something lighter, syntax-wise, where the is no need to state new and old names and types:And then the compiler lets you access in the postcondition the old variables in some standard way, like: in.exprX, in.exprY Despite this is is probably better looking: old.exprX, old.exprYout(result, exprX, exprY) {Here "old" doesn't need to be keyword, it's like the local name of a struct instance. As discussed in D.learn there is no need for deep copying of the old variables. If it's required, then the programmer has to do it manually by herself. This avoids lot of implementation complexities.Agreed. As in Eiffel, the programmer has the freedom, the responsibility AND the power to do any deep-copy work needed. For example, void foo(A a) out (auto preA = a) { /* post-condition body using preA */ } body { /* whatever foo() does */ } would not work because 'preA' is just an additional reference to the object that 'a' also refers to. If foo() happened to modify 'a.some_member', by data aliasing it has also modified 'preA.some_member', so the actual prestate is lost. You would have to do one of the following: out (auto preA = a.dup) out (immutable(A) preA = a) or use a struct for A with postblit constructor as needed (value semantics rather than reference semantics). Or drill down into the 'a' object and capture just the internal members, or expressions built from them, that you need. It's up to you. And you have the full power of the language to do it with. -- Davidson
Oct 26 2011
On 10/27/2011 07:58 AM, Davidson Corry wrote:On 10/26/2011 5:19 PM, bearophile wrote:That is what variables are for. I think that should be kept orthogonal. void generate_improved_hash_and_key(string[] plaintexts) out{ auto preFOM = old(figure_of_merit(plaintexts, internalKey, internalHaskDelegate); auto nowFOM = figure_of_merit(plaintexts, internalKey, internalHashDelegate); assert(preFOM <= nowFOM); } body{ /* ... */ } Furthermore, your example shows an extreme case and from that you conclude a general statement. That almost never works. Often it is way more convenient to just write old(exp) than 1. think of a nice name for capturing the prestate. 2. change the signature of the out contract. 3. move the cursor back to where you were before. 3. write the name again.I had intended for 'exprX' to represent some complex expression, not simply a symbol. For instance, what if the prestate that you want to capture is not merely the value-at-entry of some single datum, but rather a "meta-prestate" computed from multiple source data? For instance, suppose that you were generating hash functions and keys for a cryptographic encoder class, and you had a function figure_of_merit() [NOT a member of the class] that produces some metric for how well the encryption works, based on the results of encrypting each element of an array of sample plain-texts, and analyzing the cryptexts thus produced. The encoder class has data members 'internalKey' (a string used to decode the encrypted text) and 'internalHashDelegate' (a delegate to a hash-generating function which the class uses to encrypt plaintexts). The class also has a function generate_improved_hash_and_key() which potentially modifies either 'internalKey' or 'internalHashDelegate' or both, and you would like to verify that the function actually does improve things, or at least not make them worse: void generate_improved_hash_and_key(string[] plaintexts) out (float preFOM = figure_of_merit(plaintexts, internalKey, internalHashDelegate) ) { assert(preFOM <= figure_of_merit(plaintexts, internalKey, internalHashDelegate); } body { /* ... */ } I cannot see that the expression old.figure_of_merit(plaintexts, internalKey, internalHashDelegate) is an improvement over 'preFOM'.out(result, X preX = exprX, Y preY = exprY) { //<== prestateI'd like something lighter, syntax-wise, where the is no need to state new and old names and types:And then the compiler lets you access in the postcondition the old variables in some standard way, like: in.exprX, in.exprY Despite this is is probably better looking: old.exprX, old.exprYout(result, exprX, exprY) {
Oct 27 2011
Oooh! Now we're getting somewhere! You and bearophile have opened my eyes. On 10/27/2011 1:17 AM, Timon Gehr wrote:...your example shows an extreme case and from that you conclude a general statement. That almost never works.I merely intended to show that my proposed syntax could *handle* the extreme case, not to prove that it was necessary. The "case" I presented was not only extreme but silly, and purely for purposes of illustration. I was thinking less of syntax elegance and orthogonality, and more of easing the compiler's job. All the compiler would have to do would be to pluck (the underlined text) out(T preT = <some complete expression>) =================================== from the out "argument list" and inject it into the head of the function body, as though by mixin. However, your syntax works too:Often it is way more convenient to just write old(exp) than 1. think of a nice name for capturing the prestate. 2. change the signature of the out contract. 3. move the cursor back to where you were before. 4. write the name again.Just look for phrases of the form 'out(some complete expression)' in the post-condition body, hoist out the 'some complete expression' and prepend an 'auto fresh_symbol_x = ', then mix that into the head of the function. In fact, let us make the notation truly D-flavored: void foo(int n, string s) out { assert(count == old!count + 1, "count is wrong"); assert(!old!(test_me(n, s, count)) || test_me(n, s, count), "implication fails"); } body { // body of foo()... } gets a lowering as void foo(int n, string s) { // prestate-capture injected "mixin" auto fresh_symbol_1 = count; auto fresh_symbol_2 = test_me(n, s, count); // body of foo()... // injected post-condition tests, // with the fresh symbols substituted for the expressions assert(count == fresh_symbol_1 + 1, "count is wrong"); assert(!fresh_symbol_2 || test_me(n, s, count), "implication fails"); } This approach does not allow you to explicitly declare the type of the prestate-capture-expression (all fresh_symbol_n's are auto) nor to name it. But as you say, it also relieves you of the *requirement* to name and type the expression. And to be honest, I can't think of a situation where you would truly *need* to name an old-expression. Another advantage of the old!expression syntax is that we don't introduce an additional keyword 'old' into the language, nor do we prevent the programmer from using 'old()' as the name of a function, nor do we burden the out block with a funky "argument list". Spiffy. Thanks, guys! -- Davidson
Oct 27 2011
On 10/27/2011 10:02 PM, Davidson Corry wrote:Oooh! Now we're getting somewhere! You and bearophile have opened my eyes. On 10/27/2011 1:17 AM, Timon Gehr wrote: > ...your example shows an extreme case and from that you > conclude a general statement. That almost never works. I merely intended to show that my proposed syntax could *handle* the extreme case, not to prove that it was necessary. The "case" I presented was not only extreme but silly, and purely for purposes of illustration.Ok, then I apologize.OK, let's try this again. I was thinking less of syntax elegance and orthogonality, and more of easing the compiler's job. All the compiler would have to do would be to pluck (the underlined text) out(T preT = <some complete expression>) =================================== from the out "argument list" and inject it into the head of the function body, as though by mixin. However, your syntax works too: > Often it is way more convenient to just write old(exp) than > 1. think of a nice name for capturing the prestate. > 2. change the signature of the out contract. > 3. move the cursor back to where you were before. > 4. write the name again. Just look for phrases of the form 'out(some complete expression)' in the post-condition body, hoist out the 'some complete expression' and prepend an 'auto fresh_symbol_x = ', then mix that into the head of the function. In fact, let us make the notation truly D-flavored: void foo(int n, string s) out { assert(count == old!count + 1, "count is wrong"); assert(!old!(test_me(n, s, count)) || test_me(n, s, count), "implication fails"); } body { // body of foo()... } gets a lowering as void foo(int n, string s) { // prestate-capture injected "mixin" auto fresh_symbol_1 = count; auto fresh_symbol_2 = test_me(n, s, count); // body of foo()... // injected post-condition tests, // with the fresh symbols substituted for the expressions assert(count == fresh_symbol_1 + 1, "count is wrong"); assert(!fresh_symbol_2 || test_me(n, s, count), "implication fails"); }From the compiler's point of view, both approaches should have comparable complexity.This approach does not allow you to explicitly declare the type of the prestate-capture-expression (all fresh_symbol_n's are auto) nor to name it. But as you say, it also relieves you of the *requirement* to name and type the expression. And to be honest, I can't think of a situation where you would truly *need* to name an old-expression. Another advantage of the old!expression syntax is that we don't introduce an additional keyword 'old' into the language, nor do we prevent the programmer from using 'old()' as the name of a function, nor do we burden the out block with a funky "argument list". Spiffy. Thanks, guys! -- DavidsonYes, this design has indeed a keyword-issue that your proposal has not. I am all for not making it a keyword. The 'body' keyword imho should be removed too, there is only one place in the grammar where it is ever needed (and there it is completely redundant), therefore it could just be lexed as an identifier. That would not break any code.
Oct 27 2011
On 10/27/2011 2:12 PM, Timon Gehr wrote:Yes, this design has indeed a keyword-issue that your proposal has not. I am all for not making it a keyword. The 'body' keyword imho should be removed too, there is only one place in the grammar where it is ever needed (and there it is completely redundant), therefore it could just be lexed as an identifier. That would not break any code.Agreed. In fact, it occurred to me the other day that we could write contracts as void foo(T t) { scope(in) { // pre-condition contracts } scope(out) { // post-condition contracts } // ...body of function... } and eliminate the 'in', 'out' and 'body' keywords/constructs entirely. Similarly, an invariant could appear in the default constructor of a class as this() { scope(invariant) { // invariant guarantees... } // ...body of constructor... } Or *any* constructor, really, although you would probably want the compiler to enforce that no more than one constructor defined a scope(invariant) block. This notation also suggests (correctly) that the invariant doesn't take effect until you have entered the constructor. (Exited it, really, but...) -- Davidson
Oct 27 2011
Davidson Corry:Agreed. In fact, it occurred to me the other day that we could write contracts as void foo(T t) { scope(in) { // pre-condition contracts } scope(out) { // post-condition contracts } // ...body of function... }How do you replace out(result) with scope(out) {} ? Bye, bearophile
Oct 27 2011
On 10/27/2011 9:04 PM, bearophile wrote:I am not sure that we need to. D2 has two forms of the 'out()' statement now: 'out(result)' and 'out(...)' [as it is shown in the DPL book, or just 'out' in current practice]. The only reason to have an 'out(result)' form is to declare that we intend to use the magic 'result' symbol in the post-condition testing. Does this mean that, if we *don't* pre-declare 'out(result)' that the symbol 'result' can have some *other* meaning in the post-condition? That strikes me as a source of subtle bugs, if a programmer might accidentally fail to declare 'out(result)' and thereby get different semantics in the post-condition code without having changed that code. I think the thing to do is to just declare 'out' as we currently do [or 'scope(out)' as I am proposing], and treat the 'result' symbol magically in the post-condition code whether or not it is pre-declared -- that is, have the compiler find it and interpret it as the magic returned-value. -- DavidsonHow do you replace out(result) with scope(out) {} ?Agreed. In fact, it occurred to me the other day that we could write contracts as void foo(T t) { scope(in) { // pre-condition contracts } scope(out) { // post-condition contracts } // ...body of function... }
Oct 29 2011
Davidson Corry:The only reason to have an 'out(result)' form is to declare that we intend to use the magic 'result' symbol in the post-condition testing.This is not true. Currently 'result' has nothing magic, it's just a variable name, you are free to change it: int result; int foo(int x, out int result) out(y) { assert(y > 0); } body { result = 10; return -1; } void main() { int z; auto s = foo(4, z); }I think the thing to do is to just declare 'out' as we currently do [or 'scope(out)' as I am proposing], and treat the 'result' symbol magically in the post-condition code whether or not it is pre-declared -- that is, have the compiler find it and interpret it as the magic returned-value.In some rare situations magic is acceptable, but it's dangerous, and in most situations it's better to avoid it. Unless "result" becomes a keyword (so you can't assign something to it by mistake), then the current syntax with out(someConstVariableName) seems better. Bye, bearophile
Oct 29 2011
On 10/29/2011 8:26 PM, bearophile wrote:Ah. I did not know that. Thanks for the info.The only reason to have an 'out(result)' form is to declare that we intend to use the magic 'result' symbol in the post-condition testing.This is not true. Currently 'result' has nothing magic, it's just a variable name, you are free to change it:by mistake), then the current syntax with out(someConstVariableName) seems better. Ah, again. 'Result' *is* magic in Eiffel (in all functions). I assumed that it was equivalently magic in D (in the post-condition context only), and named that way *because* of the Eiffel usage. You learn something every day. The notion that you can name exactly zero symbols or exactly one symbol in the 'out()' "argument list", and if you name exactly one symbol then it gets (a copy of?) the returned value from the function which you can then use in your post-condition test strikes me as slightly more flexible at the cost of becoming slightly less obvious, and equally magical either way. But that's just my opinion, and I don't feel strongly about it. -- DavidsonI think the thing to do is to just declare 'out' as we currently do [or 'scope(out)' as I am proposing], and treat the 'result' symbol magically in the post-condition code whether or not it is pre-declared -- that is, have the compiler find it and interpret it as the magic returned-value.In some rare situations magic is acceptable, but it's dangerous, and in most situations it's better to avoid it.Unless "result" becomes a keyword (so you can't assign something to it
Oct 30 2011
Davidson Corry <davidsoncorry comcast.net> wrote:On 10/27/2011 2:12 PM, Timon Gehr wrote:The contracts are parts of the function's interface, not implementation. If you put the scope(in/out) inside the '{ ... }' how would a .di file handle it? And class invariant is also checked outside the constructor. Your syntax makes it look like it will run only when the constructor is called.Yes, this design has indeed a keyword-issue that your proposal has not. I am all for not making it a keyword. The 'body' keyword imho should be removed too, there is only one place in the grammar where it is ever needed (and there it is completely redundant), therefore it could just be lexed as an identifier. That would not break any code.Agreed. In fact, it occurred to me the other day that we could write contracts as void foo(T t) { scope(in) { // pre-condition contracts } scope(out) { // post-condition contracts } // ...body of function... } and eliminate the 'in', 'out' and 'body' keywords/constructs entirely. Similarly, an invariant could appear in the default constructor of a class as this() { scope(invariant) { // invariant guarantees... } // ...body of constructor... } Or *any* constructor, really, although you would probably want the compiler to enforce that no more than one constructor defined a scope(invariant) block. This notation also suggests (correctly) that the invariant doesn't take effect until you have entered the constructor. (Exited it, really, but...) -- Davidson
Oct 28 2011
On 10/28/2011 5:10 AM, kennytm wrote:Davidson Corry<davidsoncorry comcast.net> wrote:Just in passing, I think that 'scope(requires)' and 'scope(ensures)' are somewhat more evocative of what these blocks actually do than 'scope(in)' and 'scope(out)' would be. The words 'in' and 'out' have so many other connotations that they could be confusing....it occurred to me the other day that we could write contracts as void foo(T t) { scope(in) { // pre-condition contracts } scope(out) { // post-condition contracts } // ...body of function... } and eliminate the 'in', 'out' and 'body' keywords/constructs entirely.Good point. A class invariant is special enough that it should be declared at class scope, rather than inside any particular method (including constructors): class foo { scope(invariant) { /* the invariant checks */ } // implementation of the class... } As for .di files, those can declare the contracts inside the braces and simply elide the body of the function. This is similar to Eiffel's short-flat form. Eiffel doesn't use braces for statement grouping or lexical scoping, but its contracts are just as much "inside" the function or method or class as the parameter lists of those functions or methods are. Positioning the contract blocks inside the braces is also suggestive of the lexical environment (the "scope", although I'm trying not to use that word because it's overloaded in this context) within which the contract testing code runs -- anything that could be declared or referenced at that position in the source code is available to the contract code. (As with 'static if', the braces around a contract block are for grouping, and do *not* introduce a new lexical scope. You would need to {{double-brace}} the block to introduce new lexical scope, as you do with 'static if'.) ============ I do think that using the 'scope(something)' form for contracts is still a good idea. 'scope' is one of the big wins of the D design team. It allows the programmer to declare code over *here* where it's close to the resource it's going to clean up after, while making that code effective over *there* where the clean-up needs to be done -- upon exit from a block, upon failure of a block, etc. Or in this proposal, upon entry/exit to a class method (invariant) or upon entry into (requires) or exit from (ensures) a class method or a bare function... all without requiring the user to keep track of a complex scaffolding of try/catch/finally blocks, which the compiler manages for you. It has a bit of the flavor of aspect-oriented programming, but without AOP's vagueness about what's going to happen when and where. -- DavidsonThe contracts are parts of the function's interface, not implementation. If you put the scope(in/out) inside the '{ ... }' how would a .di file handle it? And class invariant is also checked outside the constructor. Your syntax makes it look like it will run only when the constructor is called.Similarly, an invariant could appear in the default constructor of a class as this() { scope(invariant) { // invariant guarantees... } // ...body of constructor... } Or*any* constructor, really, although you would probably want the compiler to enforce that no more than one constructor defined a scope(invariant) block. This notation also suggests (correctly) that the invariant doesn't take effect until you have entered the constructor. (Exited it, really, but...)
Oct 29 2011
I want to point out (quick, before someone else does!) that the lowerings I have suggested for old! elsewhere in this thread are naïve. In the presence of inheritance, interface contracts, and/or multiple subtyping, things can get notably trickier. That said, 1) I don't think it affects the utility of the old!symbol notation, and 2) old! would be useful even if inherited contracts weren't fully handled. (Much more so if they are, of course.) -- Davidson
Oct 27 2011
out(result, exprX, exprY) {If you don't need to use the method result too, then you probably write something like: out(_, exprX, exprY) { If the method is void then the _ has no value. Is this possible and good? Bye, bearophile
Oct 27 2011
On 10/27/2011 11:34 AM, bearophile wrote:This is possible, but I don't like it too much (see my other post).out(result, exprX, exprY) {If you don't need to use the method result too, then you probably write something like: out(_, exprX, exprY) { If the method is void then the _ has no value. Is this possible and good? Bye, bearophile
Oct 27 2011