www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Assigning value to a lazy parameter in a function call

reply Vidar Wahlberg <canidae exent.net> writes:
Wasn't easy to find a short good description of the issue in the 
subject, but here's some code to illustrate my "concern":
---
import std.stdio;
void log(T...)(lazy string message, lazy T t) {
         debug writefln(message, t);
}
void main() {
         int a = 42;
         writefln("The meaning of life is %s", a);
         log("Incrementing the meaning of life: %s", ++a);
         writefln("The meaning of life is now %s", a);
}
---

I often call functions where one of the parameters may be an integer 
which i post/pre increment/decrement. However, that can be quite risky 
if the parameter is defined as "lazy" as shown above.
The value of "a" above after calling "log" depends on whether you've 
compiled with "-debug" or not, this may not be very obvious and may take 
some by surprise.

Perhaps the compiler should print out a warning when you're assigning a 
value to a lazy parameter in a function call?
May 11 2012
parent reply "Chris Cain" <clcain uncg.edu> writes:
On Friday, 11 May 2012 at 20:45:53 UTC, Vidar Wahlberg wrote:
 I often call functions where one of the parameters may be an 
 integer which i post/pre increment/decrement. However, that can 
 be quite risky if the parameter is defined as "lazy" as shown 
 above.
 The value of "a" above after calling "log" depends on whether 
 you've compiled with "-debug" or not, this may not be very 
 obvious and may take some by surprise.

 Perhaps the compiler should print out a warning when you're 
 assigning a value to a lazy parameter in a function call?
The entire point of a lazy parameter is to not be calculated/processed until it's actually necessary. This is normal behavior for lazy. Most actual use cases for lazy would be made impractical if the compiler bombarded the programmer with warnings.
May 11 2012
parent reply Vidar Wahlberg <canidae exent.net> writes:
On 2012-05-11 23:03, Chris Cain wrote:
 On Friday, 11 May 2012 at 20:45:53 UTC, Vidar Wahlberg wrote:
 Perhaps the compiler should print out a warning when you're assigning
 a value to a lazy parameter in a function call?
The entire point of a lazy parameter is to not be calculated/processed until it's actually necessary. This is normal behavior for lazy. Most actual use cases for lazy would be made impractical if the compiler bombarded the programmer with warnings.
I'm not suggesting that the compiler should print a warning if you're doing a calculation in the function call, I'm suggesting it should give you a warning if you're assigning the result of the calculation to a variable in the function call. In other words, «log("%s", a + 1);» would give no warning, while «log("%s", ++a);» and «log("%s", (a = a + 1));» would. (Sorry if this is a duplicate, got an error upon sending, it appears like the message never was sent)
May 11 2012
parent reply "Chris Cain" <clcain uncg.edu> writes:
On Friday, 11 May 2012 at 21:39:57 UTC, Vidar Wahlberg wrote:
 I'm not suggesting that the compiler should print a warning if 
 you're doing a calculation in the function call, I'm suggesting 
 it should give you a warning if you're assigning the result of 
 the calculation to a variable in the function call.
 In other words, «log("%s", a + 1);» would give no warning, 
 while «log("%s", ++a);» and «log("%s", (a = a + 1));» would.
The thing is, ++a != (a = a + 1). And even if it were, it's perfectly legitimate for a lazy parameter to change the state of things outside the scope of the function. Really, lazy is just syntactic sugar for a delegate. void log(lazy string message, lazy int t) is just the same as void log(lazy string message, int delegate() t) the latter must be called like log("Incrementing the meaning of life: %s", (){return ++a;}); or log("Incrementing the meaning of life: %s", () => ++a); while the former can appear like: log("Incrementing the meaning of life: %s", ++a); In this case, I'd say that the "correct" thing to do is not have the t parameter be a lazy parameter. Generally, lazy is better used for things like the enforce provided by std.exception ... it looks something like this (I haven't seen it before, so this is an approximation based on my observations): void enforce(bool condition, lazy string msg) { if(condition) throw new EnforceException(msg); } enforce(someCondition, "Well, it looks like " ~ obj.getNameFromDatabase() ~ " is in an invalid state ... here's a helpful representation: " ~ obj.expensiveFunction()); In this case, lazy is used to prevent the expensive calls to get the name from a database and computing a hash function unless it's absolutely necessary. And obj's state might very well be changed when getNameFromDatabase is called or when expensiveFunction is run.
 (Sorry if this is a duplicate, got an error upon sending, it 
 appears like the message never was sent)
It's okay. It's a known bug right now due to the mass influx of users. It happens to all of us :)
May 11 2012
parent reply Vidar Wahlberg <canidae exent.net> writes:
On 2012-05-12 00:27, Chris Cain wrote:
 [...]
Thank you for the detailed answer. I still suspect this can fool some people, and (in my ignorance) I couldn't (and still can't, to be honest) really see when you would want to assign a variable in a lazy parameter, I would expect that to be far more often an error from the coder rather than intended behavior. I'm not here to argue though, I just wanted to express my thoughts on the issue :)
 It's okay. It's a known bug right now due to the mass influx of users.
Which is good, the language is really interesting, I hope more people will look into it.
May 11 2012
parent "Chris Cain" <clcain uncg.edu> writes:
On Friday, 11 May 2012 at 23:07:31 UTC, Vidar Wahlberg wrote:
 Thank you for the detailed answer.
 I still suspect this can fool some people, and (in my 
 ignorance) I couldn't (and still can't, to be honest) really 
 see when you would want to assign a variable in a lazy 
 parameter, I would expect that to be far more often an error 
 from the coder rather than intended behavior. I'm not here to 
 argue though, I just wanted to express my thoughts on the issue 
 :)
No problem. I hope it's become a bit more clear what lazy is for. ++a isn't assigning a. It's causing a to mutate in place (which, is just changing state). And lazy parameters are all about holding the change of state off until it's necessary. The biggest "gotcha" about lazy parameters isn't going to be that, though... it'll be something like this: -=-=- void funct(lazy int x) { writeln(x, " ", x); } void main() { int a = 0; funct(++a); // prints 1, 2 // a is now 2, even though there's only one ++a } -=-=- But keeping in mind that lazy is just convenient syntax sugar for a delegate function, the reason why this behaves like this is clearer.
May 11 2012