www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - blocks with attributes vs inlined lambda

reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent "Robert Clipsham" <robert octarineparrot.com> writes:
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
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent reply "deadalnix" <deadalnix gmail.com> writes:
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:
 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.
{} is a lambda already. The first set of () is optional.
Jun 08 2013
next sibling parent "Idan Arye" <GenericNPC gmail.com> writes:
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:
 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.
{} is a lambda already. The first set of () is optional.
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.
Jun 08 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent "monarch_dodra" <monarchdodra gmail.com> writes:
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:
 {} 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".
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 } -------
Jun 08 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
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
prev sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/6/18 monarch_dodra <monarchdodra gmail.com>

 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.
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");
 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
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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.
 ----------------
 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
Maybe you are right. I will keep using it for now.
Jun 18 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 18 June 2013 at 08:10:24 UTC, monarch_dodra 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.
Alright.
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.
Jun 19 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 30 June 2013 at 20:29:07 UTC, Timon Gehr wrote:
 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?
You may want to discuss that issue with Andrei. We had a discussion at DConf about this.
Jun 30 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/8/18 monarch_dodra <monarchdodra gmail.com>

 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?
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 Hara
Aug 18 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 19 August 2013 at 02:33:43 UTC, Kenji Hara wrote:
 2013/8/18 monarch_dodra <monarchdodra gmail.com>

 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?
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 Hara
As always, thank you *very* much.
Aug 18 2013