Local functions
John Rose
John.Rose at Sun.COM
Tue Feb 9 19:10:36 PST 2010
On Feb 9, 2010, at 6:19 PM, Neal Gafter wrote:
> On Tue, Feb 9, 2010 at 5:34 PM, Alex Buckley <Alex.Buckley at sun.com> wrote:
>> My concern about lambdas duplicating the functionality of anon.inner classes
>> has led me to think about dropping statement lambdas, keeping expression
>> lambdas, and then adding block expressions. (I am avoiding saying anything
>> about 'this' in such circumstances.)
>
> Wow. Sounds great to me. Then you don't have to specify the return
> type when you write a lambda (it is just the expression's type).
>
> You probably need a way to write a lambda with a "void" result (e.g.
> introduce a meaning for "void" in an expression context).
static void java.lang.Void.value() { } ! Alex will come up with something better. :-)
> If you go this way, you might consider adding a way to yield a result
> early from a block expression. Or, alternately, you could take the
> position that if the programmer's control flow is so complicated that
> they "need" to use a "return" statement, then they probably ought to
> use a method (perhaps in an anonymous inner class). That was where we
> ended up, but I continued to look (without success) for a good way to
> yield a result early.
By factoring block expression syntax from lambda syntax, and making the two independent, they can be independently useful. For starters, a block expression within a lambda will yield the functionality of the 0.1 statement lambda, while (conversely) a lambda inside a block expression could express a named self-recursive lambda (of the kind Josh said was sometimes necessary but not previously possible).
final (int n)->int factorial = #(int x)( (x <= 1) ? 1 : x*factorial.(x-1) ) /*picking random proposed syntaxes*/
===
final (int n)->int factorial = ({ (int n)->int f = #(int x)( (x <= 1) ? 1 : x*f.(x-1) ); f })
-- John
P.S. As has been observed, the DA rules will have to be adjusted, but there is an opportunity to do this at the boundary between a lambda and its enclosing construct. The hard case from a DA point of view is this one:
final (int)->int fn = DoSomething.toSomeFn( (int)->int (someExpression(fn)) );
The value fn can clearly be self-recursive if 'DoSomething.toSomeFn' is omitted, since the lambda will certainly not be called until 'fn' is initialized. The same is true if DoSomething.toSomeFn is some combinatory wrapper (or identity function, such as a logger) that does not actually invoke its argument. But if it does invoke its argument, a null value of 'fn' will be visible, in much the same way as a final class or instance variable can sometimes be observed to have its default initial value. This would be a "low road" type of design, but it would be consistent enough (IMO) with other quirks in Java's initialization semantics.
I think the disadvantage of potentially observing nulls is outweighed by the usefulness of recursion. Another way to avoid the problem (taken by the JavaScript standard, section 13) is to mandate a separate pre-pass for initializing function-valued variables. This only makes sense when there is a distinct syntax for functions as opposed to lambdas (as you mentioned, Neal). The special syntax would not allow any use of a combinatory wrapper.
More information about the lambda-dev
mailing list