www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - even more delegate sugar

reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
While we're at it, how about allowing the construct:
methodName (arg, arg, ..., arg, { ... });

to be equivalent to:
methodName (arg, arg, ..., arg) { ... }


and
methodName ({ ... });

to
methodName {}


Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
Arguments' would become:

void foo() {
	int x = 0;
	dotimes(10) {
		writef(x++);
	}
}


Which eliminates the need for lazy evaluation in this case, as it simply 
uses a delegate. Moreover, it is more readable and concise at the same time.


--
Tomasz Stachowiak
Aug 21 2006
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Tom S wrote:
 While we're at it, how about allowing the construct:
 methodName (arg, arg, ..., arg, { ... });
 
 to be equivalent to:
 methodName (arg, arg, ..., arg) { ... }
 
 
 and
 methodName ({ ... });
 
 to
 methodName {}
 
 
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:
 
 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }
 
 
 Which eliminates the need for lazy evaluation in this case, as it simply 
 uses a delegate. Moreover, it is more readable and concise at the same 
 time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code. But an amazing feature of your proposal is that you could write a function void If(bool b, void delegate (void) f); and then write If( cond) { writef(xxxx); } which would behave just like the built-in 'if' statement (albeit without an 'else' clause). Interesting.
Aug 22 2006
next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Don Clugston wrote:
 Tom S wrote:
 While we're at it, how about allowing the construct:
 methodName (arg, arg, ..., arg, { ... });

 to be equivalent to:
 methodName (arg, arg, ..., arg) { ... }


 and
 methodName ({ ... });

 to
 methodName {}


Just as I and others have suggested already. I really like it.
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise at 
 the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code.

Would they? (assuming there is no dotimes overload with only one argument) As I understand Toms suggestion, braces would be required when using this syntax. And if not, there is no reason allowing ; to be an empty expression in this case, just like if(10); { writef(x++); } isn't allowed in D today. The error message is: "use '{ }' for an empty statement, not a ';'"
 
 
 But an amazing feature of your proposal is that you could write
 a function
 void If(bool b, void delegate (void) f);
 
 and then write
   If( cond) { writef(xxxx); }
 
 which would behave just like the built-in 'if' statement (albeit without 
 an 'else' clause). Interesting.

Indeed. /Oskar
Aug 22 2006
parent reply Don Clugston <dac nospam.com.au> writes:
Oskar Linde wrote:
 Don Clugston wrote:
 Tom S wrote:
 While we're at it, how about allowing the construct:
 methodName (arg, arg, ..., arg, { ... });

 to be equivalent to:
 methodName (arg, arg, ..., arg) { ... }


 and
 methodName ({ ... });

 to
 methodName {}


Just as I and others have suggested already. I really like it.
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise at 
 the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code.

Would they? (assuming there is no dotimes overload with only one argument)

I was not making that assumption. There's also: dotimes(int x, void delegate(void) y = { writefln("Surprise"); }); (I think that providing a default values for any such trailing delegate would always create this situation). I don't know if this would be a big problem in practice - but it makes me a bit nervous. What would you do with methodName (int, void delegate(void) ... ) ?
 But an amazing feature of your proposal is that you could write
 a function
 void If(bool b, void delegate (void) f);

 and then write
   If( cond) { writef(xxxx); }

 which would behave just like the built-in 'if' statement (albeit 
 without an 'else' clause). Interesting.

Indeed.

On reflection, an even more interesting example is variants of foreach, which I think would become completely redundant.
Aug 22 2006
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Don Clugston wrote:
 Oskar Linde wrote:
 Don Clugston wrote:
 Tom S wrote:
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise at 
 the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code.

Would they? (assuming there is no dotimes overload with only one argument)

I was not making that assumption. There's also: dotimes(int x, void delegate(void) y = { writefln("Surprise"); }); (I think that providing a default values for any such trailing delegate would always create this situation).

You are correct, of course.
 I don't know if this would be a big problem in practice - but it makes 
 me a bit nervous.

The fact that C has had this very same issue with if, while, for, et.al. for centuries should work in favor of the proposal though.
 What would you do with
 
 methodName (int, void delegate(void) ... )
 ?

Yes, that is an interesting case. One could either: a) live with the ; changing the meaning of the code b) forbid the proposed syntax in the cases where a ; would change the meaning of the code.
 
 But an amazing feature of your proposal is that you could write
 a function
 void If(bool b, void delegate (void) f);

 and then write
   If( cond) { writef(xxxx); }

 which would behave just like the built-in 'if' statement (albeit 
 without an 'else' clause). Interesting.

Indeed.

On reflection, an even more interesting example is variants of foreach, which I think would become completely redundant.

break would pose a problem though.
Aug 22 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Oskar Linde wrote:
 Don Clugston wrote:
 Oskar Linde wrote:
 Don Clugston wrote:
 Tom S wrote:
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise 
 at the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code.

Would they? (assuming there is no dotimes overload with only one argument)

I was not making that assumption. There's also: dotimes(int x, void delegate(void) y = { writefln("Surprise"); }); (I think that providing a default values for any such trailing delegate would always create this situation).

You are correct, of course.

I think the proposed syntax should only be allowed only when the delegate param doesn't have a default value. Even if only for the sake of clarity.
 I don't know if this would be a big problem in practice - but it makes 
 me a bit nervous.

The fact that C has had this very same issue with if, while, for, et.al. for centuries should work in favor of the proposal though.
 What would you do with

 methodName (int, void delegate(void) ... )


I'm not sure what you mean with the ... there, but if you mean methodName(arg, arg, ..., arg, {}, arg, ..., arg) then perhaps it should not be allowed to use the shorthand. But more on that later.
 Yes, that is an interesting case. One could either:
 a) live with the ; changing the meaning of the code
 b) forbid the proposed syntax in the cases where a ; would change the   
 meaning of the code.

I'd opt for b)
 But an amazing feature of your proposal is that you could write
 a function
 void If(bool b, void delegate (void) f);

 and then write
   If( cond) { writef(xxxx); }

 which would behave just like the built-in 'if' statement (albeit 
 without an 'else' clause). Interesting.

Indeed.

On reflection, an even more interesting example is variants of foreach, which I think would become completely redundant.

break would pose a problem though.

Right, but it's not always needed :) Anyway, it's not the only option to go for. For instance, if the semicolon wasn't implicit, one might try some more twists, e.g.: myIf (cond) { } .myElif (cond) { } .myElse (cond) { }; Or the construct methodName([arg, ..., arg, {}], ..., [arg, ..., arg, {}]) could be allowed to expand to methodName(arg, ..., arg) { } ... (arg, ..., arg) { }; or, as Ivan Senji suggested, named args could make it become methodName(arg, ..., arg) { } namedDg = { } ... namedDg = { }; I can immediately imagine writing code like: glPushMatrix { glTranslatef(...); glBegin(GL_TRIANGLES) { glVertex(...); ... glVertex(...); } } Note that this code would also be possible in a similar form: glPushMatrix = { glTranslatef(...); glBegin(GL_TRIANGLES) = { glVertex(...); ... glVertex(...); }; }; ... just if opAssign worked for structs. /* Yet it would be noticeably harder to implement */ -- Tomasz Stachowiak
Aug 22 2006
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Oskar Linde wrote:
 Don Clugston wrote:
 Oskar Linde wrote:
 Don Clugston wrote:
 Tom S wrote:
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise 
 at the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code.

Would they? (assuming there is no dotimes overload with only one argument)

I was not making that assumption. There's also: dotimes(int x, void delegate(void) y = { writefln("Surprise"); }); (I think that providing a default values for any such trailing delegate would always create this situation).

You are correct, of course.
 I don't know if this would be a big problem in practice - but it makes 
 me a bit nervous.

The fact that C has had this very same issue with if, while, for, et.al. for centuries should work in favor of the proposal though.

Touché. I forgot about that. Indeed for (int=0;i<10;++i); { printf("%d", i); } is valid C code, and the proposal is certainly no worse than that.
 
 What would you do with

 methodName (int, void delegate(void) ... )
 ?

Yes, that is an interesting case. One could either: a) live with the ; changing the meaning of the code b) forbid the proposed syntax in the cases where a ; would change the meaning of the code.

It would also be nice to be able to do something with the related example: methodName(int x, void delegate(void) dg[] ... ) but I don't think it's practical. methodName(7) { writefln("first"); } with { writefln("second")} with { writefln("third"); } Unconvincing.
 On reflection, an even more interesting example is variants of 
 foreach, which I think would become completely redundant.

break would pose a problem though.

You're right. Although foreach with opApply has the same problem.
Aug 22 2006
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Don Clugston wrote:
 Oskar Linde wrote:
 Don Clugston wrote:
 On reflection, an even more interesting example is variants of 
 foreach, which I think would become completely redundant.

break would pose a problem though.

You're right. Although foreach with opApply has the same problem.

Umm, nope... http://digitalmars.com/d/statement.html#foreach Look at the opApply example. The delegate implementing foreach's body returns != 0 in case of a break. It can be emulated thru e.g. return BREAK; or return CONTINUE; but if there was a way to define a function like: outer.return myBreak() { outer.return BREAK; } ... then things could get even more bizarre ;)
Aug 22 2006
parent Don Clugston <dac nospam.com.au> writes:
Tom S wrote:
 Don Clugston wrote:
 Oskar Linde wrote:
 Don Clugston wrote:
 On reflection, an even more interesting example is variants of 
 foreach, which I think would become completely redundant.

break would pose a problem though.

You're right. Although foreach with opApply has the same problem.

Umm, nope... http://digitalmars.com/d/statement.html#foreach Look at the opApply example. The delegate implementing foreach's body returns != 0 in case of a break.

That's exactly what I meant. You can't use 'break' inside opApply, but of course it can be emulated, and the same solution could apply to Foreach function. It's just lacking in syntactic sugar (and probably in performance, too).
 It can be emulated thru e.g.
 return BREAK;
 or
 return CONTINUE;

 
 
 but if there was a way to define a function like:
 
 outer.return myBreak() {
     outer.return BREAK;
 }
 
 ... then things could get even more bizarre ;)

Aug 22 2006
prev sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Don Clugston wrote:
 Tom S wrote:
 While we're at it, how about allowing the construct:
 methodName (arg, arg, ..., arg, { ... });

 to be equivalent to:
 methodName (arg, arg, ..., arg) { ... }


 and
 methodName ({ ... });

 to
 methodName {}


 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:

 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }


 Which eliminates the need for lazy evaluation in this case, as it 
 simply uses a delegate. Moreover, it is more readable and concise at 
 the same time.

Sounds nice, but nowhere near enough visual cues. If you leave off a semicolon, the meaning completely changes. dotimes(10); { writef(x++); } and dotimes(10) { writef(x++); } would both be valid code. But an amazing feature of your proposal is that you could write a function void If(bool b, void delegate (void) f); and then write If( cond) { writef(xxxx); } which would behave just like the built-in 'if' statement (albeit without an 'else' clause). Interesting.

And if we could name arguments at call site then a function: void If(bool c, void delegate (void) Then, void delegate (void) Else = null); could be called like this: If(cond) Then = { writef(xxxx); } Else = { writef(xxxxx); } ;) Wouldn't that be cool? :P
Aug 22 2006
prev sibling parent Walter Bright <newshound digitalmars.com> writes:
Tom S wrote:
 While we're at it, how about allowing the construct:
 methodName (arg, arg, ..., arg, { ... });
 
 to be equivalent to:
 methodName (arg, arg, ..., arg) { ... }
 
 
 and
 methodName ({ ... });
 
 to
 methodName {}
 
 
 Then e.g. the 'dotimes' example from 'Lazy Evaluation of Function 
 Arguments' would become:
 
 void foo() {
     int x = 0;
     dotimes(10) {
         writef(x++);
     }
 }
 
 
 Which eliminates the need for lazy evaluation in this case, as it simply 
 uses a delegate. Moreover, it is more readable and concise at the same 
 time.

It is a good idea and I've bandied it around before, and it has its merits. One of its problems is it only works for cases where the delegate is the last parameter. Can't have arrays of delegates like the example in http://www.digitalmars.com/d/lazy-evaluation.html. It also doesn't work where one wants the lazy evaluation function to return a value.
Aug 22 2006