digitalmars.D - Why callers should explicitly document storage classes
- Mehrdad (25/25) Aug 14 2011 Consider this example:
- Lutger Blijdestijn (14/43) Aug 14 2011 I'm not convinced that intimately is the right word here. Lazy evaluatio...
- Timon Gehr (7/32) Aug 14 2011 Basically, as I see it what is wrong is that the function is called
- Vladimir Panteleev (7/32) Aug 14 2011 I've raised this issue during voting for std.parallelism. I think the
- Timon Gehr (6/41) Aug 14 2011 requiring lazy before lazy arguments basically destroys the reason for
- Vladimir Panteleev (5/10) Aug 14 2011 What about requiring "lazy" only for non-pure delegates?
- Timon Gehr (3/13) Aug 14 2011 Actually I would rather require lazy arguments to be pure, so that they
- Jonathan M Davis (5/20) Aug 14 2011 That still wouldn't be guaranteed, since pure function calls are only
- Timon Gehr (4/24) Aug 14 2011 My point was, that if lazy arguments were required to be pure, such a
- Jonathan M Davis (15/44) Aug 14 2011 Yes. In theory. It would depend on what Walter was willing to do with th...
- Brad Roberts (13/58) Aug 14 2011 The pure keyword doesn't _change_ semantics, but it very much affects
- kennytm (16/33) Aug 14 2011 One problem: It is expected that the lazy argument can be re-evaluated. ...
- Simen Kjaeraas (6/16) Aug 14 2011 I also thought of this idea. Now that pure inference is (being) added to
- Jonathan M Davis (9/77) Aug 14 2011 Oh, I know that it's not always 1, and I'm not suggesting that it always...
Consider this example: // In module foo.d: void baz(bool condition, lazy int value) { if (condition) writeln(value); } // In module bar.d: import foo; bool someCondition() { return false; } void main() { auto vals = [1, 2, 3, 4]; while (!vals.empty) baz(someCondition(), items.moveFront()); } There is **absolutely NO WAY** to figure out what's wrong at the calling site. You need to check /every/ method call and make sure nothing weird is happening (e.g. lazy), and it's pretty much impossible to figure it out unless you're intimately familiar with the entire library you're calling -- something which (obviously) doesn't scale. I don't know of a similar example off the top of my head for out/ref, but the same idea applies. Is this convincing enough that we need to document storage classes at the CALLING site, rather than just the CALLEE site?
Aug 14 2011
Mehrdad wrote:Consider this example: // In module foo.d: void baz(bool condition, lazy int value) { if (condition) writeln(value); } // In module bar.d: import foo; bool someCondition() { return false; } void main() { auto vals = [1, 2, 3, 4]; while (!vals.empty) baz(someCondition(), items.moveFront()); } There is **absolutely NO WAY** to figure out what's wrong at the calling site. You need to check /every/ method call and make sure nothing weird is happening (e.g. lazy), and it's pretty much impossible to figure it out unless you're intimately familiar with the entire library you're calling -- something which (obviously) doesn't scale.I'm not convinced that intimately is the right word here. Lazy evaluation is a pretty big deal in the interface of a function, just like ref is. If you have no idea at all what a function does, there is no hope to figure out what the code at the call site does to begin with. Even without lazy/out/ref you could be passing an object with reference semantics to a function which mutates the object, same deal. Not everything is 'const correct', so this may also be surprising if you do not know the function.I don't know of a similar example off the top of my head for out/ref, but the same idea applies. Is this convincing enough that we need to document storage classes at the CALLING site, rather than just the CALLEE site?Do you mean literally documenting it or having the compiler require you to state the storage class? If the first, I'd say it depends on the context. The enforce function in phobos for example is clear and well known enough to leave it out. I would not want the latter. But yes, if it's not clear enough then it's a good idea. Preferably it would be clear from the function name and purpose how it operates on the parameters though.
Aug 14 2011
On 08/14/2011 01:34 PM, Mehrdad wrote:Consider this example: // In module foo.d: void baz(bool condition, lazy int value) { if (condition) writeln(value); } // In module bar.d: import foo; bool someCondition() { return false; } void main() { auto vals = [1, 2, 3, 4]; while (!vals.empty) baz(someCondition(), items.moveFront()); } There is **absolutely NO WAY** to figure out what's wrong at the calling site. You need to check /every/ method call and make sure nothing weird is happening (e.g. lazy), and it's pretty much impossible to figure it out unless you're intimately familiar with the entire library you're calling -- something which (obviously) doesn't scale. I don't know of a similar example off the top of my head for out/ref, but the same idea applies. Is this convincing enough that we need to document storage classes at the CALLING site, rather than just the CALLEE site?Basically, as I see it what is wrong is that the function is called "baz" (and maybe that lazy arguments are allowed to be side-effecting). out/ref: int x=100; foo(x); assert(x==100);
Aug 14 2011
On Sun, 14 Aug 2011 14:34:46 +0300, Mehrdad <wfunction hotmail.com> wrote:Consider this example: // In module foo.d: void baz(bool condition, lazy int value) { if (condition) writeln(value); } // In module bar.d: import foo; bool someCondition() { return false; } void main() { auto vals = [1, 2, 3, 4]; while (!vals.empty) baz(someCondition(), items.moveFront()); } There is **absolutely NO WAY** to figure out what's wrong at the calling site. You need to check /every/ method call and make sure nothing weird is happening (e.g. lazy), and it's pretty much impossible to figure it out unless you're intimately familiar with the entire library you're calling -- something which (obviously) doesn't scale. I don't know of a similar example off the top of my head for out/ref, but the same idea applies. Is this convincing enough that we need to document storage classes at the CALLING site, rather than just the CALLEE site?I've raised this issue during voting for std.parallelism. I think the consensus was that "lazy" before lazy arguments would be appropriate, but "ref" would be mostly pointless, due to complex and reference types. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Aug 14 2011
On 08/14/2011 05:36 PM, Vladimir Panteleev wrote:On Sun, 14 Aug 2011 14:34:46 +0300, Mehrdad <wfunction hotmail.com> wrote:requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});Consider this example: // In module foo.d: void baz(bool condition, lazy int value) { if (condition) writeln(value); } // In module bar.d: import foo; bool someCondition() { return false; } void main() { auto vals = [1, 2, 3, 4]; while (!vals.empty) baz(someCondition(), items.moveFront()); } There is **absolutely NO WAY** to figure out what's wrong at the calling site. You need to check /every/ method call and make sure nothing weird is happening (e.g. lazy), and it's pretty much impossible to figure it out unless you're intimately familiar with the entire library you're calling -- something which (obviously) doesn't scale. I don't know of a similar example off the top of my head for out/ref, but the same idea applies. Is this convincing enough that we need to document storage classes at the CALLING site, rather than just the CALLEE site?I've raised this issue during voting for std.parallelism. I think the consensus was that "lazy" before lazy arguments would be appropriate, but "ref" would be mostly pointless, due to complex and reference types.
Aug 14 2011
On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr <timon.gehr gmx.ch> wrote:requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates? -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Aug 14 2011
On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr <timon.gehr gmx.ch> wrote:Actually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On Sunday, August 14, 2011 22:25:36 Timon Gehr wrote:On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:That still wouldn't be guaranteed, since pure function calls are only optimized out if they're strongly pure and in the same expression. You can't rely on calls to pure functions being optimized. - Jonathan M DavisOn Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr <timon.gehr gmx.ch> wrote:Actually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On 08/14/2011 10:36 PM, Jonathan M Davis wrote:On Sunday, August 14, 2011 22:25:36 Timon Gehr wrote:My point was, that if lazy arguments were required to be pure, such a thing _could_ then be guaranteed independently from optimizations that may or may not take place on general pure functions.On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:That still wouldn't be guaranteed, since pure function calls are only optimized out if they're strongly pure and in the same expression. You can't rely on calls to pure functions being optimized. - Jonathan M DavisOn Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr<timon.gehr gmx.ch> wrote:Actually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On Sunday, August 14, 2011 23:20:12 Timon Gehr wrote:On 08/14/2011 10:36 PM, Jonathan M Davis wrote:wrote:On Sunday, August 14, 2011 22:25:36 Timon Gehr wrote:On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr<timon.gehr gmx.ch>Yes. In theory. It would depend on what Walter was willing to do with the language and the compiler though. At present, pure is purely an optimization and not use to affect semantics at all. However, I'd actually argue that lazy delegates should be effectively treated as pure anyway. Conceptually, what you're doing is delaying the execution of the expression in case it isn't needed. I don't see any reason why the end result should be any different from passing a non-lazy argument. As such, the lazy argument should only be called once regardless. It wouldn't surprise me at all if the implementation doesn't work that way, but I'd argue that it should regardless of purity. From the standpoint of the function, the lazy argument is just like any other argument in terms of its value, and IMHO it should be treated that way. - Jonathan M DavisMy point was, that if lazy arguments were required to be pure, such a thing _could_ then be guaranteed independently from optimizations that may or may not take place on general pure functions.That still wouldn't be guaranteed, since pure function calls are only optimized out if they're strongly pure and in the same expression. You can't rely on calls to pure functions being optimized. - Jonathan M DavisActually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On Sunday, August 14, 2011 2:30:39 PM, Jonathan M Davis wrote:On Sunday, August 14, 2011 23:20:12 Timon Gehr wrote:s being optimized.On 08/14/2011 10:36 PM, Jonathan M Davis wrote:wrote:On Sunday, August 14, 2011 22:25:36 Timon Gehr wrote:On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr<timon.gehr gmx.ch>That still wouldn't be guaranteed, since pure function calls are only optimized out if they're strongly pure and in the same expression. You can't rely on calls to pure functionActually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?The pure keyword doesn't _change_ semantics, but it very much affects semantics, through restriction and the guarantees it provides. The restrictions help enforce some very specific and broadly useful behaviors. But you know this.Yes. In theory. It would depend on what Walter was willing to do with the language and the compiler though. At present, pure is purely an optimization and not use to affect semantics at all.- Jonathan M DavisMy point was, that if lazy arguments were required to be pure, such a thing _could_ then be guaranteed independently from optimizations that may or may not take place on general pure functions.However, I'd actually argue that lazy delegates should be effectively treated as pure anyway. Conceptually, what you're doing is delaying the execution of the expression in case it isn't needed. I don't see any reason why the end result should be any different from passing a non-lazy argument. As such, thelazy argument should only be called once regardless. It wouldn't surprise me at all if the implementation doesn't work that way, but I'd argue that it should regardless of purity. From the standpoint of the function, the lazy argument is just like any other argument in terms of its value, and IMHO it should be treated that way.That's not the way it behaves, or is defined to behave. Lazy parameters are executed 0-N times, entirely based on the implementation of the method itself. It _could_ be re-done to mean 0 or 1, but that's still not always 1. If it was, might as well kill the lazy concept.- Jonathan M Davis
Aug 14 2011
Timon Gehr <timon.gehr gmx.ch> wrote:On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:One problem: It is expected that the lazy argument can be re-evaluated. D's `lazy` is actually call-by-name, which allowed stuff like ---- void dotimes(int count, lazy void exp) { for (int i = 0; i < count; i++) exp(); } void foo() { int x = 0; dotimes(10, writef(x++)); } ---- as documented in http://www.d-programming-language.org/lazy-evaluation.htmlOn Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr <timon.gehr gmx.ch> wrote:Actually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On Sun, 14 Aug 2011 22:00:34 +0200, Vladimir Panteleev <vladimir thecybershadow.net> wrote:On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr <timon.gehr gmx.ch> wrote:I also thought of this idea. Now that pure inference is (being) added to the language, this seems doable and good. -- Simenrequiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?
Aug 14 2011
On Sunday, August 14, 2011 17:39:29 Brad Roberts wrote:On Sunday, August 14, 2011 2:30:39 PM, Jonathan M Davis wrote:Oh, I know that it's not always 1, and I'm not suggesting that it always be 1. I'm suggesting that it always be 0 or 1, and that it not actually be called multiple times even if it's referenced multiple times. I don't see why there is any value in having it called multiple times. In concept at least, a non- lazy parameter is the same as a non-lazy except that it isn't evaluated unless it's used. And so I don't see why you'd want to be evaluating it multiple times except that it's probably easier to implement that way. - Jonathan M DavisOn Sunday, August 14, 2011 23:20:12 Timon Gehr wrote:s being optimized.On 08/14/2011 10:36 PM, Jonathan M Davis wrote:wrote:On Sunday, August 14, 2011 22:25:36 Timon Gehr wrote:On 08/14/2011 10:00 PM, Vladimir Panteleev wrote:On Sun, 14 Aug 2011 22:48:18 +0300, Timon Gehr<timon.gehr gmx.ch>That still wouldn't be guaranteed, since pure function calls are only optimized out if they're strongly pure and in the same expression. You can't rely on calls to pure functionActually I would rather require lazy arguments to be pure, so that they can be guaranteed to be executed at most once.requiring lazy before lazy arguments basically destroys the reason for lazy being in the language: int foo(lazy 2*3); is not better than int foo({return 2*3});What about requiring "lazy" only for non-pure delegates?The pure keyword doesn't _change_ semantics, but it very much affects semantics, through restriction and the guarantees it provides. The restrictions help enforce some very specific and broadly useful behaviors. But you know this.Yes. In theory. It would depend on what Walter was willing to do with the language and the compiler though. At present, pure is purely an optimization and not use to affect semantics at all.- Jonathan M DavisMy point was, that if lazy arguments were required to be pure, such a thing _could_ then be guaranteed independently from optimizations that may or may not take place on general pure functions.However, I'd actually argue that lazy delegates should be effectively treated as pure anyway. Conceptually, what you're doing is delaying the execution of the expression in case it isn't needed. I don't see any reason why the end result should be any different from passing a non-lazy argument. As such, thelazy argument should only be called once regardless. It wouldn't surprise me at all if the implementation doesn't work that way, but I'd argue that it should regardless of purity. From the standpoint of the function, the lazy argument is just like any other argument in terms of its value, and IMHO it should be treated that way.That's not the way it behaves, or is defined to behave. Lazy parameters are executed 0-N times, entirely based on the implementation of the method itself. It _could_ be re-done to mean 0 or 1, but that's still not always 1. If it was, might as well kill the lazy concept.
Aug 14 2011