www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - nested functions and closures

reply Johannes Pfau <nospam example.com> writes:
When debugging gdc I've found some at least for me interesting
behavior which I'm trying to understand now:

----
auto testFunction() {
    int a = 41;
    int nestedFunc() { return ++a; }

    struct NestedStruct {
        int answer() { return nestedFunc(); }
    }

    return NestedStruct();
}

writeln(testFunction().answer());
----

This is valid D code, creates a closure.

----
class testClass
{
    int a = 41;
    int nestedFunc() { return ++a; }

    struct NestedStruct {
        int answer() { return nestedFunc(); }
    }
    
    auto getStruct() { return NestedStruct(); }
}

writeln((new TestClass()).getStruct().answer);
----

This does not compile:
test.d(33): Error: this for nestedFunc needs to be type testClass not
type NestedStruct

Is this intended? It seems to be very similar to the function closure
code. Couldn't NestedStruct just get a pointer to the testClass to
access its variables? Or am I missing something?

Everything works fine if NestedStruct is a class and not a struct so I
guess this functionality was disabled deliberately. But why? And why
wasn't using a nested struct in functions in a similar way disallowed as
well?
Feb 11 2013
next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Monday, 11 February 2013 at 18:59:37 UTC, Johannes Pfau wrote:
 When debugging gdc I've found some at least for me interesting
 behavior which I'm trying to understand now:

 ----
 auto testFunction() {
     int a = 41;
     int nestedFunc() { return ++a; }

     struct NestedStruct {
         int answer() { return nestedFunc(); }
     }

     return NestedStruct();
 }

 writeln(testFunction().answer());
 ----

 This is valid D code, creates a closure.

 ----
 class testClass
 {
     int a = 41;
     int nestedFunc() { return ++a; }

     struct NestedStruct {
         int answer() { return nestedFunc(); }
     }
 
     auto getStruct() { return NestedStruct(); }
 }

 writeln((new TestClass()).getStruct().answer);
 ----

 This does not compile:
 test.d(33): Error: this for nestedFunc needs to be type 
 testClass not
 type NestedStruct

 Is this intended? It seems to be very similar to the function 
 closure
 code. Couldn't NestedStruct just get a pointer to the testClass 
 to
 access its variables? Or am I missing something?

 Everything works fine if NestedStruct is a class and not a 
 struct so I
 guess this functionality was disabled deliberately. But why? 
 And why
 wasn't using a nested struct in functions in a similar way 
 disallowed as
 well?

I recently wrote an article partly on this topic which may be of interest to you. Apologies if it is not. See part one: neutrons. http://forum.dlang.org/thread/ririagrqecshjljcdubd forum.dlang.org
Feb 11 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 11 Feb 2013 13:59:36 -0500, Johannes Pfau <nospam example.com>  
wrote:

 When debugging gdc I've found some at least for me interesting
 behavior which I'm trying to understand now:

 ----
 auto testFunction() {
     int a = 41;
     int nestedFunc() { return ++a; }

     struct NestedStruct {
         int answer() { return nestedFunc(); }
     }

     return NestedStruct();
 }

 writeln(testFunction().answer());
 ----

 This is valid D code, creates a closure.

 ----
 class testClass
 {
     int a = 41;
     int nestedFunc() { return ++a; }

     struct NestedStruct {
         int answer() { return nestedFunc(); }
     }
    auto getStruct() { return NestedStruct(); }
 }

 writeln((new TestClass()).getStruct().answer);
 ----

 This does not compile:
 test.d(33): Error: this for nestedFunc needs to be type testClass not
 type NestedStruct

 Is this intended? It seems to be very similar to the function closure
 code. Couldn't NestedStruct just get a pointer to the testClass to
 access its variables? Or am I missing something?

 Everything works fine if NestedStruct is a class and not a struct so I
 guess this functionality was disabled deliberately. But why? And why
 wasn't using a nested struct in functions in a similar way disallowed as
 well?

A nested class has an outer pointer to it's owner class. You cannot instantiate a nested class without it's owner class. A nested struct does NOT have a pointer to it's owner class. It's simply typed inside the class' namespace. FYI, nested classes were enshrined with a pointer to an outer instance for two reasons: 1. Their footprint is larger, not as big a hit to add another pointer. 2. To allow porting of Java code. Structs are POD for the most part, and are much more "bare metal" than classes. Nested structs could be given an instance pointer to the owner, but I think we would need a new construct for that. For your problem at hand, you may want to consider using interfaces instead. Or you can possibly embed the owner pointer manually. -Steve
Feb 11 2013
prev sibling next sibling parent Johannes Pfau <nospam example.com> writes:
Am Mon, 11 Feb 2013 17:03:22 -0500
schrieb "Steven Schveighoffer" <schveiguy yahoo.com>:

 
 A nested class has an outer pointer to it's owner class.  You cannot  
 instantiate a nested class without it's owner class.
 
 A nested struct does NOT have a pointer to it's owner class.  It's
 simply typed inside the class' namespace.
 
 FYI, nested classes were enshrined with a pointer to an outer
 instance for two reasons:
 
 1. Their footprint is larger, not as big a hit to add another pointer.
 2. To allow porting of Java code.
 
 Structs are POD for the most part, and are much more "bare metal"
 than classes.
 
 Nested structs could be given an instance pointer to the owner, but
 I think we would need a new construct for that.

Thanks, I thought it was something like that. The weird part is that if you return a nested struct which is nested in a _function_ the compiler generates a closure for the function and then struct is given an instance pointer to the closure. But if you return a struct nested in a class the struct doesn't get an instance pointer.
 For your problem at hand, you may want to consider using interfaces  
 instead.  Or you can possibly embed the owner pointer manually.
 
 -Steve

Fortunately there's no problem at hand, I was just curious :-) I actually use nested classes/structs and closures quite rarely.
Feb 12 2013
prev sibling next sibling parent Johannes Pfau <nospam example.com> writes:
Am Mon, 11 Feb 2013 21:41:26 +0100
schrieb "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com>:

 
 I recently wrote an article partly on this topic which may be of 
 interest to you. Apologies if it is not. See part one: neutrons.
 
 http://forum.dlang.org/thread/ririagrqecshjljcdubd forum.dlang.org

Yes, that's basically the same question. I'm not really interested in possible implementation details though, I'd just like to know why it wasn't allowed to access outer variables of nested structs in the first place. But Steven Schveighoffer answer already covers that.
Feb 12 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 12 Feb 2013 03:23:13 -0500, Johannes Pfau <nospam example.com>  
wrote:

 Am Mon, 11 Feb 2013 17:03:22 -0500
 schrieb "Steven Schveighoffer" <schveiguy yahoo.com>:

 A nested class has an outer pointer to it's owner class.  You cannot
 instantiate a nested class without it's owner class.

 A nested struct does NOT have a pointer to it's owner class.  It's
 simply typed inside the class' namespace.

 FYI, nested classes were enshrined with a pointer to an outer
 instance for two reasons:

 1. Their footprint is larger, not as big a hit to add another pointer.
 2. To allow porting of Java code.

 Structs are POD for the most part, and are much more "bare metal"
 than classes.

 Nested structs could be given an instance pointer to the owner, but
 I think we would need a new construct for that.

Thanks, I thought it was something like that. The weird part is that if you return a nested struct which is nested in a _function_ the compiler generates a closure for the function and then struct is given an instance pointer to the closure. But if you return a struct nested in a class the struct doesn't get an instance pointer.

If structs inside functions acted just like structs outside of functions, there would be very little reason to have them. Functions sort of have their own namespace, but I don't know if that justifies having function-nested structs. There are static function-nested structs, but I think the concept of using them with frequency is pretty new (voldemort types). I actually wouldn't mind an opt-in approach to including an outer reference, at least for structs nested in classes (it's too dangerous to do this for owner structs). I think it's too late to make that the default.
 For your problem at hand, you may want to consider using interfaces
 instead.  Or you can possibly embed the owner pointer manually.

 -Steve

Fortunately there's no problem at hand, I was just curious :-) I actually use nested classes/structs and closures quite rarely.

I use nested structs for namespace in dcollections quite frequently. For instance TreeMap.cursor is a nested struct. -Steve
Feb 12 2013