Jump to page: 1 2 3
Thread overview
Idea: "Frozen" inner function
Nov 24, 2006
Michael Butscher
Nov 25, 2006
David Medlock
Nov 25, 2006
Steve Horne
Nov 25, 2006
Michael Butscher
Nov 26, 2006
Michael Butscher
Nov 26, 2006
Steve Horne
Nov 26, 2006
Michael Butscher
Nov 26, 2006
Michael Butscher
Nov 27, 2006
Steve Horne
Nov 27, 2006
Steve Horne
Nov 27, 2006
Michael Butscher
Nov 27, 2006
Steve Horne
Nov 28, 2006
Michael Butscher
Nov 27, 2006
David Medlock
Nov 28, 2006
Michael Butscher
Nov 28, 2006
David Medlock
Nov 25, 2006
Marcio
Nov 25, 2006
Andrey Khropov
November 24, 2006
Hi,

this is yet another idea how to return delegates from a function without destroying the context (which could be on the stack).


An inner function could get "frozen" as some kind of storage class which means that all variables accessed by the inner function which are defined in the surrounding function keep the value they had at the point in time when the inner function was defined.

Technically the context pointer of the inner function would point to an area on the heap. When the point of definition of the inner function is reached (which can happen multiple times) the needed variables are copied from the stackframe of the outer function to the context area of the inner function on the heap.



Example:


import std.stdio;

void main()
{
    int i = 0;

    while (i < 10)
    {
        frozen void pr() // when reached, i is copied to context
        {
            writef(i, " ");
        }

        ++i;  // pr() doesn't care about this change
        pr();
    }
}

Without "frozen" this prints:
1 2 3 4 5 6 7 8 9 10

With "frozen" this would print:
0 1 2 3 4 5 6 7 8 9


Of course you would put the definition only inside such a loop if you need it for some special reason.



Maybe it would also be desirable to trigger the copying of stackframe data at another point than function definition.

For this, a property "freeze" could be created for the inner function, so the above code could also look like:


import std.stdio;

void main()
{
    int i = 0;

    frozen void pr()
    {
        writef(i, " ");
    }

    while (i < 10)
    {
        pr.freeze;
        ++i;  // pr() doesn't care about this change
        pr();
    }
}


But this might be more complicated as the scope at function definition could be another than at the "freeze" call.



Michael
November 25, 2006
Michael Butscher wrote:
> Hi,
> 
> 
> Michael

These are a good idea.  These are called closures.

-DavidM
November 25, 2006
On Fri, 24 Nov 2006 21:59:33 +0100, Michael Butscher <mbutscher@gmx.de> wrote:

>An inner function could get "frozen" as some kind of storage class which means that all variables accessed by the inner function which are defined in the surrounding function keep the value they had at the point in time when the inner function was defined.

It's called a closure.

D inner functions seem to borrow a lot from the Pascal family languages. These have inner functions, but don't have closures. It simply isn't the done thing to call these inner functions using a pointer after the outer function had exited - it isn't part of structured programming.

Closures originally came about in languages that have 'first class functions' - functional languages. These days, some scripting languages have them - Python uses them for 'lambda' anonymous functions, for instance.

But it is a trade-off between efficiency and flexibility. D currently goes with efficiency by default, but you can explicitly create an inner function with a closure if you need one - though its not very convenient.

Tip - use an anonymous class with a function-call method.

Of course, a shorthand for doing this is a good idea.

-- 
Remove 'wants' and 'nospam' from e-mail.
November 25, 2006
Steve Horne wrote:
> On Fri, 24 Nov 2006 21:59:33 +0100, Michael Butscher <mbutscher@gmx.de> wrote:
> 
> >An inner function could get "frozen" as some kind of storage class which means that all variables accessed by the inner function which are defined in the surrounding function keep the value they had at the point in time when the inner function was defined.
> 
> It's called a closure.
> 
> D inner functions seem to borrow a lot from the Pascal family languages. These have inner functions, but don't have closures. It simply isn't the done thing to call these inner functions using a pointer after the outer function had exited - it isn't part of structured programming.
> 
> Closures originally came about in languages that have 'first class functions' - functional languages. These days, some scripting languages have them - Python uses them for 'lambda' anonymous functions, for instance.

I heard about closures already, but I thought that they are different from inner functions only by surviving the end of the outer function.

Especially a closure would have direct access to the current value of local variables of the outer function (if it exists yet) which is not the case for frozen functions.


> But it is a trade-off between efficiency and flexibility. D currently goes with efficiency by default, but you can explicitly create an inner function with a closure if you need one - though its not very convenient.

The only efficiency tradeoff of frozen functions is the copying at the time of definition (and later the freeing of the context memory). Except for that they would be as efficient as inner functions.


> Tip - use an anonymous class with a function-call method.

This would be technically the same, but, as you wrote, less convenient.



Michael
November 25, 2006
Michael Butscher wrote:
> Hi,
> 
> this is yet another idea how to return delegates from a function without destroying the context (which could be on the stack).


	Related: the way #Smalltalk implements blocks on .NET:

http://www.refactory.com/Software/SharpSmalltalk/Implementation.html


marcio
November 25, 2006
"Michael Butscher" <mbutscher@gmx.de> wrote in message news:MPG.1fd2668cdc5c74998969d@news.digitalmars.com...

> The only efficiency tradeoff of frozen functions is the copying at the time of definition (and later the freeing of the context memory). Except for that they would be as efficient as inner functions.

If in a loop, or in a function which is called a lot, that would be a lot of allocations, which is particularly wasteful if you never return the closure from the function.  I'd say a very large percentage of the time, you don't need to return nested functions, so it'd be wasteful to enable the feature when it's not used that often.

Additionally, there's the problem of functions with several levels of nesting.  If you have c() inside b() inside a(), if c() accesses local variables of both b() and a(), when b() returns (but still inside a()), the closure of c() will have to be able to access the active locals of a() on the stack, but the locals of b() will have to be on the heap.  This would have to be done through some second level of indirection or using multiple context pointers.  This problem exists in languages such as Lua (and mine, MiniD!) and is handled with tricky things called upvalues, which are a second level of indirection for each outer function local.  I don't know how well something like that would fit into a lower-level language like D.

> This would be technically the same, but, as you wrote, less convenient.

For the time being, though, it's certainly a nice (and simple) alternative / workaround.  It also has the advantage that you can very selectively control if/when the closure's context is allocated on the heap.


November 25, 2006
Walter expressed his opinion in this thread:

http://www.digitalmars.com/d/archives/digitalmars/D/40600.html

-- 
AKhropov
November 26, 2006
On Sat, 25 Nov 2006 14:41:49 +0100, Michael Butscher <mbutscher@gmx.de> wrote:

>Especially a closure would have direct access to the current value of local variables of the outer function (if it exists yet) which is not the case for frozen functions.

That's not the definition of closures I was taught. A closure would behave exactly as your frozen function does - it does not have direct access to the current value since it keeps a copy of the value at the time when the definition was 'executed'.

So for instance, in Python (which does use closures)...

a = 5
fn = lambda : a
a = 6
print fn()

The output should be 5, not 6 - the value when the lambda expression was evaluated.

Is it possible that you're being misled by the style of closures used for generics? They tend to mostly hold function pointers (pointers to the methods for the type that the generic is specialised for), but could possibly hold pointers to global variables too.

-- 
Remove 'wants' and 'nospam' from e-mail.
November 26, 2006
This is the case for Java also. If you define a anonymous class in a method it can access the local variable and arguments of the surrounding method. The compiler forces them to be final.

This is exactly like finding all references to the surrounding method, and make a copy of all accessed vars.

e.g.

class MyClass{
  int    fld_i;
  double fld_d;
  void f( final int i, double d ){
    ListenerType l = new ListenerType(){
      void event( ){
        fld_i = i; // OK
        fld_i++;   // OK
        i++;       // Error, i is final
        fld_d = d; // Error, can only access final variables
      }
    }
  }
}


November 26, 2006
Jarrett Billingsley wrote:
> "Michael Butscher" <mbutscher@gmx.de> wrote in message news:MPG.1fd2668cdc5c74998969d@news.digitalmars.com...
> 
> > The only efficiency tradeoff of frozen functions is the copying at the time of definition (and later the freeing of the context memory). Except for that they would be as efficient as inner functions.
> 
> If in a loop, or in a function which is called a lot, that would be a lot of allocations, which is particularly wasteful if you never return the closure from the function.

It is up to the programmer to place the definition of the "closure" at a point where it is only executed if needed, e.g. it could be placed directly before it is returned by the outer function. The allocation of the heap could be executed only when reaching the definition of the frozen function.

My example might have been a bit misleading.


>  I'd say a very large percentage of the time, you don't
> need to return nested functions, so it'd be wasteful to enable the feature
> when it's not used that often.

One field of application would also be to create small event handlers for GUI events. This happens more often, I think.

But maybe it is really not needed so often, but I remember that there were already some discussions about closures, so I wanted to propose this simpler alternative.


> Additionally, there's the problem of functions with several levels of nesting.  If you have c() inside b() inside a(), if c() accesses local variables of both b() and a(), when b() returns (but still inside a()), the closure of c() will have to be able to access the active locals of a() on the stack, but the locals of b() will have to be on the heap.

If c() would be defined frozen, it just has all variables declared in c
() itself on the stack and all variables declared somewhere outside on
the heap (as a copy). It does especially NOT access the active locals
of a(), but the locals as they were when c() was defined.

If the original outside-variables exist on the stack yet is totally irrelevant.


>  This would
> have to be done through some second level of indirection or using multiple
> context pointers.  This problem exists in languages such as Lua (and mine,
> MiniD!) and is handled with tricky things called upvalues, which are a
> second level of indirection for each outer function local.  I don't know how
> well something like that would fit into a lower-level language like D.

Therefore my proposal is much simpler to realize than that.


> > This would be technically the same, but, as you wrote, less convenient.
> 
> For the time being, though, it's certainly a nice (and simple) alternative / workaround.  It also has the advantage that you can very selectively control if/when the closure's context is allocated on the heap.

But I also have to remember which outer variables are accessed. If I change the code of the inner function I could forget to copy a variable or copy unnecessary variables.

My idea is not new but just more convenient and less error prone than the anonymous class technique.



Michael
« First   ‹ Prev
1 2 3