Recursive lambdas
Alex Buckley
Alex.Buckley at Sun.COM
Mon Feb 22 02:20:11 PST 2010
The ability for a lambda expression to reference itself during
initialization is noted in the 0.1.5 spec draft, but not actually
specified. However, the answer is clear: the value of a variable of
function type is a reference. fac.(...) makes reference to the lambda
instance resulting from evaluation of #(int x)(x<=1...). This holds
whether fac is a local variable or an instance variable. As always,
extravagant aliasing is best avoided.
Alex
Alex Blewitt wrote:
> It has been suggested that recursive lambdas can be defined without any assumption of 'this' as follows:
>
> #int(int) fac = #(int x)(x <= 1 ? 1 : x * fac.(x-1));
>
> There may be confusion in the case of 'fac' being overwritten in the remainder of the body:
>
> #int(int) fac = #(int x)(x <= 1 ? 1 : x * fac.(x-1));
> #int(int) other = #(int x)( fac.(x) ); // Not used in this example, see below
> #int(int) old = fac;
> fac = #(int x)(0);
> old.()//
>
> What does (should) old.() result in? In the case of a lambda-this, the answer would be clear; it would return the factorial as before. However, ther are two ways of interpreting the execution of old.() here:
>
> * The embedded 'fac' retains its old reference from when it was defined
> * The embedded 'fac' is interpreted in the current scope, and so returns 0
>
> This is a contrived example, but now let's hoist these to member variables of a class:
>
> class Foo {
> #int(int) fac = #(int x)(x <= 1 ? 1 : x * fac.(x-1));
> #int(int) other = #(int x)( fac.(x) );
> Foo() {
> #int(int) old = fac;
> fac = #(int x)(0);
> old.(); // What does this return?
> }
> }
>
> One might argue that it's the same result. But in this case, 'fac' can refer to one of two things:
>
> * The lambda-this
> * The reference to this.fac as a field in the class
>
> Under the first interpretation, the effect is the same as before - it prints the factorial correctly. However, under the second interpretation, it will print 0.
>
> The 'other' here demonstrates the problem. We have two lambdas, both with recursive calls to fac.(x) in them; however, the 'other' will always refer to the field 'fac' in the enclosing scope by reference rather than by value (will it?) Mark's example yesterday suggests that it might:
>
> class Foo {
> Runnable r = new Runnable() {
> public void run() {
> r.run(); // refers to Foo.this.r, and so will see changes to r
> }
> };
> }
>
> One can special-case this (or treat it as an oddity that no-one will worry about) but I thought I'd bring it up in the result of Mark's post.
>
> Alex
>
More information about the lambda-dev
mailing list