digitalmars.D - blocks with attributes vs inlined lambda
- monarch_dodra (28/28) Jun 08 2013 I remember think that being able to mark specific parts of code
- monarch_dodra (27/27) Jun 08 2013 EDIT: Sorry: stupid computer sent before I was finished. I was
- Robert Clipsham (32/40) Jun 18 2013 Here's my go at cleaning up the syntax a bit:
- deadalnix (3/31) Jun 08 2013 Sound like a nice idiom. Why is the first set of () needed ? Can
- monarch_dodra (10/12) Jun 08 2013 It's because you are actually declaring a lambda that takes no
- deadalnix (2/16) Jun 08 2013 {} is a lambda already. The first set of () is optional.
- Idan Arye (8/27) Jun 08 2013 Actually, the first set of `()` is required because of the
- monarch_dodra (14/15) Jun 08 2013 Oh Yeah. Didn't know that. You learn something new every day.
- monarch_dodra (13/19) Jun 08 2013 Improved example:
- monarch_dodra (52/55) Jun 17 2013 I've been playing around with this idiom some more, and found
- deadalnix (3/14) Jun 17 2013 In case of lambda, purity does the wrong thing. The behavior is
- Kenji Hara (14/69) Jun 18 2013 In D, variable declaration with default construction is always nothrow.
- monarch_dodra (6/33) Jun 18 2013 Right. Good point.
- monarch_dodra (12/18) Jun 19 2013 I did some benching, and they aren't getting inline (well... I
- Timon Gehr (3/28) Jun 30 2013 Great. Does the fix also introduce the 'immutable' storage class for
- deadalnix (3/9) Jun 30 2013 You may want to discuss that issue with Andrei. We had a
- monarch_dodra (8/13) Aug 18 2013 Kenji:
- Kenji Hara (7/19) Aug 18 2013 I opened a new bugzilla issue:
- monarch_dodra (2/32) Aug 18 2013 As always, thank you *very* much.
I remember think that being able to mark specific parts of code with certain attributes, inside a block, could be very useful. for example: -------- void foo() { //Code /Critical section nothrow { //code here can't throw } //More code } -------- I have since learned that you can do this with lambda's instead, declaring and calling them in a single line: -------- void foo() { //Code /Critical section () nothrow { //code here can't throw } //More code } --------
Jun 08 2013
EDIT: Sorry: stupid computer sent before I was finished. I was saying... I have since learned that you can do this with lambda's instead, declaring and calling them in a single line: -------- void foo() { //Code //Critical section () nothrow //declaration { //body //code here can't throw } () //actual call //More code } -------- This works. This feature is appearing with higher frequency inside phobos, which is proof it is a good feature, and that it has uses, and is becoming popular. My beef though is that the syntax is a bit opaque, and not really idiomatic. It looks more like a hacker's trick than a real language feature. It's not something you'd do "naturally". At least, I know a beginner may not feal comfortable with this. Given that users are now actually doing this, that we have proof it works and is a useful feature, shouldn't we push to have actual blocks with attributes in the language? I think we should... Thoughts?
Jun 08 2013
On Saturday, 8 June 2013 at 14:57:17 UTC, monarch_dodra wrote:My beef though is that the syntax is a bit opaque, and not really idiomatic. It looks more like a hacker's trick than a real language feature. It's not something you'd do "naturally". At least, I know a beginner may not feal comfortable with this. Given that users are now actually doing this, that we have proof it works and is a useful feature, shouldn't we push to have actual blocks with attributes in the language? I think we should... Thoughts?Here's my go at cleaning up the syntax a bit: ---- auto block(string attr)() { static struct Blk { mixin(q{ void opBinary(string op:"in", T)(T dg) } ~ attr ~ q{ if (is(typeof(() } ~ attr ~ q{ { dg(); }))) { dg(); } void opBinary(string op:"in", T)(T dg) if (!is(typeof(() } ~ attr ~ ` { dg(); }))) { static assert(false, "Provided delegate is not " ~ "` ~ attr ~ `"); } `); } return Blk(); } void main() { // Works block!"nothrow" in { // Doesn't throw }; // Fails block!"nothrow" in { throw new Exception(""); }; } ---- Downsides are the required semicolon (needed for your method too) and abuse of operator overloading.
Jun 18 2013
On Saturday, 8 June 2013 at 14:52:23 UTC, monarch_dodra wrote:I remember think that being able to mark specific parts of code with certain attributes, inside a block, could be very useful. for example: -------- void foo() { //Code /Critical section nothrow { //code here can't throw } //More code } -------- I have since learned that you can do this with lambda's instead, declaring and calling them in a single line: -------- void foo() { //Code /Critical section () nothrow { //code here can't throw } //More code } --------Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?
Jun 08 2013
On Saturday, 8 June 2013 at 15:56:47 UTC, deadalnix wrote:Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?It's because you are actually declaring a lambda that takes no arguments, and has no name, and contains a body. BTW, you quoted my incomplete code, the correct code I wanted to post was: () nothrow {//anonymous lambade declaration //Body of the lambde }() //actual call to lambda I'd want to be able to do this, without either the first or last set of (). The required change of grammar would mean there'd be no lambda at all afterwards.
Jun 08 2013
On Saturday, 8 June 2013 at 16:13:28 UTC, monarch_dodra wrote:On Saturday, 8 June 2013 at 15:56:47 UTC, deadalnix wrote:{} is a lambda already. The first set of () is optional.Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?It's because you are actually declaring a lambda that takes no arguments, and has no name, and contains a body. BTW, you quoted my incomplete code, the correct code I wanted to post was: () nothrow {//anonymous lambade declaration //Body of the lambde }() //actual call to lambda I'd want to be able to do this, without either the first or last set of (). The required change of grammar would mean there'd be no lambda at all afterwards.
Jun 08 2013
On Saturday, 8 June 2013 at 16:21:26 UTC, deadalnix wrote:On Saturday, 8 June 2013 at 16:13:28 UTC, monarch_dodra wrote:Actually, the first set of `()` is required because of the `nothrow`. You can't write: nothrow { //body }(); because the `nothrow` in lambdas needs to be placed after the argument list - so you need an argument list.On Saturday, 8 June 2013 at 15:56:47 UTC, deadalnix wrote:{} is a lambda already. The first set of () is optional.Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?It's because you are actually declaring a lambda that takes no arguments, and has no name, and contains a body. BTW, you quoted my incomplete code, the correct code I wanted to post was: () nothrow {//anonymous lambade declaration //Body of the lambde }() //actual call to lambda I'd want to be able to do this, without either the first or last set of (). The required change of grammar would mean there'd be no lambda at all afterwards.
Jun 08 2013
On Saturday, 8 June 2013 at 16:21:26 UTC, deadalnix wrote:{} is a lambda already. The first set of () is optional.Oh Yeah. Didn't know that. You learn something new every day. ...but for that to work, (it seems) you have actually store or pass the lambda, or all the parser sees is a plain "block". ------- void main() { int i; auto y = {++i;}; //lambde (not called) {++i;}; //simple block (){++i;}(); //inline lambda call writeln(i); //prints 2 } -------
Jun 08 2013
On Saturday, 8 June 2013 at 17:27:48 UTC, monarch_dodra wrote:On Saturday, 8 June 2013 at 16:21:26 UTC, deadalnix wrote:Improved example: ------- void main() { int i; auto y = {++i;}; //lambde (not called) {++i;}; //simple block (){++i;}(); //inline lambda call // {++i;}(); //Un-recognized writeln(i); //prints 2 } -------{} is a lambda already. The first set of () is optional.Oh Yeah. Didn't know that. You learn something new every day. ...but for that to work, (it seems) you have actually store or pass the lambda, or all the parser sees is a plain "block".
Jun 08 2013
On Saturday, 8 June 2013 at 15:56:47 UTC, deadalnix wrote:On Saturday, 8 June 2013 at 14:52:23 UTC, monarch_dodra wrote: Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?I've been playing around with this idiom some more, and found some pretty serious limitations: Using a lambda functions creates a new scope. Having a "true" labeled block, just like static ifs, should not create a new scope. This is relevant if you want to make sure a constructor or destructor is nothrow, for example (destructor could be handled via a simple attribute followed by a colon. eg: void foo() { nothrow { Sentinel s; //declare sentinel } code_goes_here nothrow: clean_up_goes_here_code destructors_are_nothrow } A lambda would not allow these two constructs. Because a lambda needs to access the frame, any function using the lambda trick can't be made pure: void foo(T)(T a) safe { (){ ++a; }(); } Error: pure function 'main.foo' cannot call impure function literal '__lambda1' The workaround is to explicitly pass the arguments, *preferably*, shadowing them for clarity: void foo(int a) pure { (ref int a){ ++a; }(a); } This is already *much* less convenient. Imagine if the block needed to access 3, 5 or even more variabes ! Also, if one of those variables is declared as "auto", then you bring in the "ref typeof(a) a" ugliness. Just no. I haven't actually tested this one, but I'd be inclined to think there is a performance hit for non-release and non-inline builds. inline builds *may* optimize it away, but I'm not sure... ---------------- So my conclusion is that the lambda tric is a partial workaround. We'd need real support for being able to have specific qualification inside bodies. I've decided to write a DIP for this.
Jun 17 2013
On Monday, 17 June 2013 at 19:05:07 UTC, monarch_dodra wrote:Because a lambda needs to access the frame, any function using the lambda trick can't be made pure: void foo(T)(T a) safe { (){ ++a; }(); } Error: pure function 'main.foo' cannot call impure function literal '__lambda1'In case of lambda, purity does the wrong thing. The behavior is bogous.
Jun 17 2013
2013/6/18 monarch_dodra <monarchdodra gmail.com>On Saturday, 8 June 2013 at 15:56:47 UTC, deadalnix wrote:In D, variable declaration with default construction is always nothrow. So, enclosing the declaration of s by nothrow block is unnecessary. For nothrow destruction, you can add following static assert in foo(). static assert(__traits(compiles, ()nothrow{ Sentinel s; }), "Sentinel dtor is not nothrow");On Saturday, 8 June 2013 at 14:52:23 UTC, monarch_dodra wrote: Sound like a nice idiom. Why is the first set of () needed ? Can we change the grammar to remove them ?I've been playing around with this idiom some more, and found some pretty serious limitations: Using a lambda functions creates a new scope. Having a "true" labeled block, just like static ifs, should not create a new scope. This is relevant if you want to make sure a constructor or destructor is nothrow, for example (destructor could be handled via a simple attribute followed by a colon. eg: void foo() { nothrow { Sentinel s; //declare sentinel } code_goes_here nothrow: clean_up_goes_here_code destructors_are_nothrow } A lambda would not allow these two constructs.Because a lambda needs to access the frame, any function using the lambda trick can't be made pure: void foo(T)(T a) safe { (){ ++a; }(); } Error: pure function 'main.foo' cannot call impure function literal '__lambda1' The workaround is to explicitly pass the arguments, *preferably*, shadowing them for clarity: void foo(int a) pure { (ref int a){ ++a; }(a); } This is already *much* less convenient. Imagine if the block needed to access 3, 5 or even more variabes ! Also, if one of those variables is declared as "auto", then you bring in the "ref typeof(a) a" ugliness. Just no.This is a compiler bug, and I recently fixed it in git master. Explicit argument passing does not need anymore.I haven't actually tested this one, but I'd be inclined to think there is a performance hit for non-release and non-inline builds. inline builds *may* optimize it away, but I'm not sure...Inlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.---------------- So my conclusion is that the lambda tric is a partial workaround. We'd need real support for being able to have specific qualification inside bodies.I don't think so. We can sufficiently use lambda for the "attribute block". Kenji Hara
Jun 18 2013
On Tuesday, 18 June 2013 at 07:58:06 UTC, Kenji Hara wrote:In D, variable declaration with default construction is always nothrow. So, enclosing the declaration of s by nothrow block is unnecessary.Well, what about construction with args...For nothrow destruction, you can add following static assert in foo(). static assert(__traits(compiles, ()nothrow{ Sentinel s; }), "Sentinel dtor is not nothrow");Right. Good point.This is a compiler bug, and I recently fixed it in git master. Explicit argument passing does not need anymore.Cool, thanks.Inlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.Alright.Maybe you are right. I will keep using it for now.---------------- So my conclusion is that the lambda tric is a partial workaround. We'd need real support for being able to have specific qualification inside bodies.I don't think so. We can sufficiently use lambda for the "attribute block". Kenji Hara
Jun 18 2013
On Tuesday, 18 June 2013 at 08:10:24 UTC, monarch_dodra wrote:I did some benching, and they aren't getting inline (well... I don't *think* they are): Code with an inline lambda call is definitely slower (in my case, with no args passed). My tests are somewhat synthetic, (I'm testing a rather short function with a lambda) but the overall slowdown is quite noticeable. I don't worry about it much, but I guess these could scale in the grand scheme of things, and is a bit vexing when all you wanted to was mark something as trusted :/ Shouldn't an inlined lambda call *always* be... inlined? I really don't see a reason not to do so, but I'm outside my field of competence at that point.Inlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.Alright.
Jun 19 2013
On 06/18/2013 09:57 AM, Kenji Hara wrote:Because a lambda needs to access the frame, any function using the lambda trick can't be made pure: void foo(T)(T a) safe { (){ ++a; }(); } Error: pure function 'main.foo' cannot call impure function literal '__lambda1' The workaround is to explicitly pass the arguments, *preferably*, shadowing them for clarity: void foo(int a) pure { (ref int a){ ++a; }(a); } This is already *much* less convenient. Imagine if the block needed to access 3, 5 or even more variabes ! Also, if one of those variables is declared as "auto", then you bring in the "ref typeof(a) a" ugliness. Just no. This is a compiler bug, and I recently fixed it in git master. Explicit argument passing does not need anymore.Great. Does the fix also introduce the 'immutable' storage class for function literals for the case where a strongly pure literal is wanted?
Jun 30 2013
On Sunday, 30 June 2013 at 20:29:07 UTC, Timon Gehr wrote:You may want to discuss that issue with Andrei. We had a discussion at DConf about this.This is a compiler bug, and I recently fixed it in git master. Explicit argument passing does not need anymore.Great. Does the fix also introduce the 'immutable' storage class for function literals for the case where a strongly pure literal is wanted?
Jun 30 2013
On Tuesday, 18 June 2013 at 07:58:06 UTC, Kenji Hara wrote:Inlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.Kenji: I've been doing some benchmarks recently: Using an "inlined lambda" seems to really kill performance, both with or without "-inline" (tested with both dmd and gdc). However, using a named function, and then immediately calling it, there is 0 performance penalty (both w/ and w/o -inline). Is this a bug? Can it be fixed? Should I file and ER?
Aug 18 2013
2013/8/18 monarch_dodra <monarchdodra gmail.com>On Tuesday, 18 June 2013 at 07:58:06 UTC, Kenji Hara wrote:I opened a new bugzilla issue: http://d.puremagic.com/issues/show_bug.cgi?id=10848 And start working for the compiler and doc fix: https://github.com/D-Programming-Language/dlang.org/pull/372 https://github.com/D-Programming-Language/dmd/pull/2483 Kenji HaraInlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.Kenji: I've been doing some benchmarks recently: Using an "inlined lambda" seems to really kill performance, both with or without "-inline" (tested with both dmd and gdc). However, using a named function, and then immediately calling it, there is 0 performance penalty (both w/ and w/o -inline). Is this a bug? Can it be fixed? Should I file and ER?
Aug 18 2013
On Monday, 19 August 2013 at 02:33:43 UTC, Kenji Hara wrote:2013/8/18 monarch_dodra <monarchdodra gmail.com>As always, thank you *very* much.On Tuesday, 18 June 2013 at 07:58:06 UTC, Kenji Hara wrote:I opened a new bugzilla issue: http://d.puremagic.com/issues/show_bug.cgi?id=10848 And start working for the compiler and doc fix: https://github.com/D-Programming-Language/dlang.org/pull/372 https://github.com/D-Programming-Language/dmd/pull/2483 Kenji HaraInlining should remove performance penalty. Nobody holds the immediately called lambda, so it should be treated as a 'scope delegate'. For that, we would need to add a section in language spec to support it.Kenji: I've been doing some benchmarks recently: Using an "inlined lambda" seems to really kill performance, both with or without "-inline" (tested with both dmd and gdc). However, using a named function, and then immediately calling it, there is 0 performance penalty (both w/ and w/o -inline). Is this a bug? Can it be fixed? Should I file and ER?
Aug 18 2013