new Java lambda syntax

M

markspace

Just thought some folks might be interested in this:


Date: Thu, 08 Sep 2011 16:07:03 -0400
From: Brian Goetz <[email protected]>
To: "(e-mail address removed)" <[email protected]>


"This just in: the EG [Expert Group] has (mostly) made a decision on syntax.

After considering a number of alternatives, we decided to essentially
adopt the C# syntax. We may still deliberate further on the fine points
(e.g., thin arrow vs fat arrow, special nilary form, etc), and have not
yet come to a decision on method reference syntax.

The C# syntax is:

lambda = ArgList Arrow Body
ArgList = Identifier
| "(" Identifier [ "," Identifier ]* ")"
| "(" Type Identifier [ "," Type Identifier ]* ")"
Body = Expression
| "{" [ Statement ";" ]+ "}"

Here are some examples of lambda expressions using this syntax:

x => x + 1
(x) => x + 1
(int x) => x + 1
(int x, int y) => x + y
(x, y) => x + y
(x, y) => { System.out.printf("%d + %d = %d%n", x, y, x+y); }
() => { System.out.println("I am a Runnable"); }

The decision to choose this syntax was twofold:
- The syntax scores "pretty well" on most subjective measures (though
has cases where it looks bad, just like all the others do). In
particular, it does well with "small" lambdas that are used as method
arguments (a common case), and also does well with large
(multi-statement) lambdas.

- Despite extensive searching, there was no clear winner among the
alternatives (each form had some good aspects and some really not very
good aspects, and there was no form that was clearly better than the
others). So, we felt that it was better to choose something that has
already been shown to work well in the two languages that are most like
Java -- C# and Scala -- rather than to invent something new.

A compiler implementation should be available soon."
 
R

Roedy Green

x => x + 1
(x) => x + 1

That seems backwards to me. Surely you really mean
x <= x + 1

But <= is already taken. So you would have to use something like the
Algol := or <- or one of the Unicode arrows.

--
Roedy Green Canadian Mind Products
http://mindprod.com
The modern conservative is engaged in one of man's oldest exercises in moral philosophy; that is,
the search for a superior moral justification for selfishness.
~ John Kenneth Galbraith (born: 1908-10-15 died: 2006-04-29 at age: 97)
 
A

Arne Vajhøj

That seems backwards to me. Surely you really mean
x<= x + 1

But<= is already taken. So you would have to use something like the
Algol := or<- or one of the Unicode arrows.

They wrote:

#So, we felt that it was better to choose something that has
#already been shown to work well in the two languages that are most like
#Java -- C# and Scala -- rather than to invent something new.

So do you think they meant to use the C# and Scala syntax or not??

Arne
 
B

BGB

That seems backwards to me. Surely you really mean
x<= x + 1

But<= is already taken. So you would have to use something like the
Algol := or<- or one of the Unicode arrows.

well, I think the point is that they chose the C# syntax, which uses "=>".


granted, this would probably not have been my first choice (my own
language uses the syntax "fun(args...) body").

so:
"fun(x)x+1"
or (more verbose/explicit):
"fun(x:int):int { return x+1; }"

(note: "x:int" is because my language uses JS/AS style declaration
syntax...).


but, I can't really fault the Java people either (and also introducing a
new keyword is not free).

granted, I hadn't seen what other options they had considered (I don't
subscribe to the relevant mailing list), but if some of them had
involved simply reusing/abusing an existing keyword:
"final(x) x+1" or "new(x) x+1" or similar...

I wouldn't blame them too much (Java doesn't have a whole lot of good
choice WRT reserved words which could be overloaded here).


and, if it were compared to the C++0x lambda syntax ("[](int x)->int
{return x+1; }"), I am happy enough with their choice (because the C++0x
syntax is IMO "teh barf...").


or such...
 
A

Arne Vajhøj

well, I think the point is that they chose the C# syntax, which uses "=>".

granted, this would probably not have been my first choice (my own
language uses the syntax "fun(args...) body").

so:
"fun(x)x+1"
or (more verbose/explicit):
"fun(x:int):int { return x+1; }"

(note: "x:int" is because my language uses JS/AS style declaration
syntax...).

As a curiosum then C# also allows:

delegate(int x) { return x+1; }

Arne
 
B

BGB

As a curiosum then C# also allows:

delegate(int x) { return x+1; }

this was their older syntax, before they added "=>", why? who knows...

delegate would work, except it is a question if the Java people would
have wanted to add this keyword either...
 
N

Nasser M. Abbasi

That seems backwards to me. Surely you really mean
x<= x + 1

But<= is already taken. So you would have to use something like the
Algol := or<- or one of the Unicode arrows.

Maple uses the exact same syntax above (but uses -> not =>).

Here is an example

|\^/| Maple 14 (IBM INTEL NT)
.._|\| |/|_. Copyright (c) Maplesoft, a division of Waterloo Maple Inc. 2010
\ MAPLE / All rights reserved. Maple is a trademark of
foo:=x->x^2;
2
foo := x -> x
100


--Nasser
 
A

Arne Vajhøj

this was their older syntax, before they added "=>", why? who knows...

delegate would work, except it is a question if the Java people would
have wanted to add this keyword either...

It was added in 2.0 before lambda was added in (C#) 3.0,
but it still works.

And the keyword delegate has been in C# since 1.0, so it was
natural there.

Arne
 
J

Joshua Cranmer

That seems backwards to me. Surely you really mean
x<= x + 1

No, the idea of a lambda is it's a "function" that converts a tuple of
(x) to a tuple of (x + 1), so the value x becomes x + 1 in the end. The
left-facing arrow instead is more evocative of "assigning" x + 1 to x,
which is why it gets occasional use as an assignment operator. I suspect
<=-ish operators are also more common in purer functional languages, but
I am no authority there.
 
B

BGB

[...]
As a curiosum then C# also allows:

delegate(int x) { return x+1; }

this was their older syntax, before they added "=>", why? who knows...

The "=>" syntax was introduced as part of a broader application, the
more general lambda expression feature. In certain contexts, a C# lambda
expression becomes an anonymous method, but in other contexts, it
becomes a specific kind of expression type.

The "delegate" syntax predates the generalized lambda expression and is
usable _only_ for anonymous methods. In that context, it makes a lot of
sense, because anonymous methods can be referenced only via a delegate
instance, so reusing the keyword to express that gives some continuity.

fair enough...

I haven't seen anything about the syntax for a new function pointer type
in Java to use with a lambda. Maybe that's what the message from Goetz
means when he writes "…have not yet come to a decision on method
reference syntax". But it's possible that _some_ new keyword will yet
need to be added.

I wouldn't know, I don't subscribe to that list, so I have no idea what
is going on there.

Whether that's "delegate" or something else, who knows? (Well, okay…I'll
bet someone does. I just haven't been keeping up on the issue, so _I_
don't know).

"int (*foo)(int x);"
ok, maybe not...


in my own language, I use the 'typedef' keyword in a manner similar to
C#'s use of the 'delegate' keyword (and my language uses 'delegate' for
something almost entirely different).


but, possible could be something like:

typedef int FooMethodType(int x);
....
FooMethodType assignableMethod;
....
assignableMethod = (int x)=>x+1;
or:
assignableMethod = FooMethodType=>x+1;


in my own syntax, it would be more like:
typedef function FooMethodType(x);
....
var assignableMethod:FooMethodType;
....
assignableMethod = fun(x) x+1;

although, possibly nice:
assignableMethod = FooMethodType=>x+1;


where, in my language, typedef is technically a modifier (it is not a
syntactic form, and also has no effect on parsing in any way, unlike in
C or C++).


now, what do I use 'delegate' for?
for inter-object delegation (technically, it is a scoping feature).

basically, it makes object variables "transparent", so that the
fields/methods/... located within the referenced objects may be accessed
as if they were part of the current scope (basically, sort of like
"static import" but with object instances and variables, and it may
apply recursively).

attempting to access a variable or call a method may forward the request
to the object in question (with some edge-case semantics WRT instance
methods, but I will not really go into this). note that it still
respects 'private' and similar.


technically, in this language, packages are themselves implemented as
objects (and imports are internally implemented using variables linking
to said package-objects).

implicitly, "this" may be considered as itself analogous to a delegate
variable, but my language allows implementing more variables with
similar behavior (and also allows building a "toplevel" out of a
patchwork of linked object instances).


or such...
 
T

Tom Anderson

"This just in: the EG [Expert Group] has (mostly) made a decision on syntax.

After considering a number of alternatives, we decided to essentially
adopt the C# syntax. We may still deliberate further on the fine points
(e.g., thin arrow vs fat arrow, special nilary form, etc), and have not
yet come to a decision on method reference syntax.

Syntax, shmyntax. Have they settled on the semantics? Did all that
appalling lambda-transparency Gafterist nonsense get ditched or enshrined?
Did that weird stuff about automatic conversion to single-method
interfaces make it through?
A compiler implementation should be available soon."

And, one hopes, a draft of the JLS change.

Thanks for bringing this to our attention, mark.

tom
 
S

Steven Simpson

Did all that appalling lambda-transparency Gafterist nonsense get
ditched or enshrined?

My understanding is that the problem scope has been gradually whittled
down to 'potentially concurrent' APIs, i.e. where a method's contract
does not make the guarantee that all calls to a supplied lambda will be
on the method caller's thread, or that they will all finish before the
method call returns. These are the sorts of guarantees you'd need to do
control abstraction or event-driven coding, which in turn need the extra
transparencies.

I think the only transparencies that exist now are:

* 'effectively final' - A non-final local variable can be immutably
captured by a lambda, so long as it's shown not to be assigned to
subsequently.
* 'long this' - In the body of a lambda, 'this' has the same meaning
as it would in the enclosing scope. It does not refer to the
object that ultimately fulfils the lambda.
* 'throws T' - A set of exceptions can be expressed as a generic
type parameter, so they can be relayed from the lambda's signature
to the signature of the method that calls it.

There's no mutable local capture, or long jumps (break, continue,
return, throw). I'd like to have seen such things, but I've come to
agree that they shouldn't exist generally in lambdas.

It could be argued that these restrictions seem to reduce lambdas to
just a shorter syntax for certain anonymous inner classes. However, I
think there's an aspiration to implement them more cheaply than normal
objects.
Did that weird stuff about automatic conversion to single-method
interfaces make it through?

There are no automatically generated families of function types (yet),
so you have to rely on SAM (single abstract method) types. Lambdas are
just a syntactic construct until you've expressed or implied the SAM
type, with an assignment, initialization or a cast.
 
T

Tom Anderson

My understanding is that the problem scope has been gradually whittled
down to 'potentially concurrent' APIs, i.e. where a method's contract
does not make the guarantee that all calls to a supplied lambda will be
on the method caller's thread, or that they will all finish before the
method call returns. These are the sorts of guarantees you'd need to do
control abstraction or event-driven coding, which in turn need the extra
transparencies.

Yes - that sounds very sensible. It gives you lambdas that are as robust
as objects, in terms of being used from multiple threads and so on, which
is a good thing. Having lambdas have weaker guarantees would have
introduced a second, parallel, set of rules which programmers would have
had to learn, which would have been a bad thing. I'm surprised to hear (by
implication) that 'non-potentially concurrent' semantics were even
considered.

Assuming i'm understanding you correctly.
I think the only transparencies that exist now are:

* 'effectively final' - A non-final local variable can be immutably
captured by a lambda, so long as it's shown not to be assigned to
subsequently.

Seems sensible. I would have been happy with a requirement for explicit
finality, but i recognise that many people would have been annoyed by it,
and i don't think this introduces any danger. Any idea how hard it is for
the compiler to prove that the variable cannot be modified?
* 'long this' - In the body of a lambda, 'this' has the same meaning
as it would in the enclosing scope. It does not refer to the
object that ultimately fulfils the lambda.

Also seems sensible. Are there any cases where you would want to refer to
the lambda object itself? I'm sure people will come up with them once
lambdas come into use. Will there be any way to get hold of such a
reference?
* 'throws T' - A set of exceptions can be expressed as a generic
type parameter, so they can be relayed from the lambda's signature
to the signature of the method that calls it.

Oh, cool.
There's no mutable local capture,
Good.

or long jumps (break, continue, return, throw).

Good! That was the thing i disliked most.
I'd like to have seen such things, but I've come to agree that they
shouldn't exist generally in lambdas.

This seems to be a chararacteristic of the birth of lambdas in Java.
Everyone started out passionately wanting different things, but we've been
worn down into more or less agreeing on something.
It could be argued that these restrictions seem to reduce lambdas to
just a shorter syntax for certain anonymous inner classes. However, I
think there's an aspiration to implement them more cheaply than normal
objects.

I'd be happy with them actually being syntactic sugar for anonymous
classes (i am quite unsophisticated my tastes!). The VM boffins could then
focus on making anonymous classes cheaper in general.
There are no automatically generated families of function types (yet),
so you have to rely on SAM (single abstract method) types. Lambdas are
just a syntactic construct until you've expressed or implied the SAM
type, with an assignment, initialization or a cast.

Oh, wait, what? Wow. There's no function type? So you can *only* use
lambdas as SAMs? Have i understood that correctly? That's kinky. You say
'yet' - can we expect function types before it's finished?

tom
 
S

Steven Simpson

Seems sensible. I would have been happy with a requirement for
explicit finality, but i recognise that many people would have been
annoyed by it, and i don't think this introduces any danger. Any idea
how hard it is for the compiler to prove that the variable cannot be
modified?

Hmm, it's more strict than I thought (but easier to compile). It seems
you have to be able to add final to the variable declaration. This
compiles fine:

void func() {
int i = 0;

Runnable action = () -> { System.out.println(i); };
}

....but any assignment to 'i', before or after creating 'action',
prevents 'i' from being effectively final.

Hardly a disaster. :)
Also seems sensible. Are there any cases where you would want to refer
to the lambda object itself? I'm sure people will come up with them
once lambdas come into use. Will there be any way to get hold of such
a reference?

Some corner cases came up, along with some work-arounds. They were
probably recursive. I think assignment rules were altered slightly so
that a lambda body could refer to itself:

Runnable x = () -> { x.run(); };

It was deemed okay to allow this because you can be certain that x will
be ready before it is run.
Oh, cool.

Yes, except I'm not sure how useful it will be in practice with
concurrent APIs. With a serial contract, like a library version of the
for-each loop, it's clearly useful:

interface Block<E, throws T> {
void apply(E val) throws T;
}

static<E, throws T> void forEach(Iterable<E> coll, Block<E, T> block) throws T {
for (Iterator<E> iter = coll.iterator(); iter.hasNext(); ) {
E elem = iter.next();
block.apply(elem);
}
}

Now the forEach method can generically throw the same set of exceptions
T that the block itself can throw.

But if there is no serial guarantee on invoking the block, you have to
choose whether you return arbitrarily just the first exception thrown,
or pack them into some structure, or something else. I've a feeling
that, most of the time, such methods will require the SAM type to throw
nothing said:
I'd be happy with them actually being syntactic sugar for anonymous
classes (i am quite unsophisticated my tastes!). The VM boffins could
then focus on making anonymous classes cheaper in general.

I think they don't foresee such savings for anon classes generally,
because they potentially have fields and multiple user-defined methods.
When you're certain such complexities don't exist, which is the case for
lambdas, certain optimizations become possible.
Oh, wait, what? Wow. There's no function type? So you can *only* use
lambdas as SAMs? Have i understood that correctly? That's kinky.

You seem disturbingly excited by that! :) Here are some arguments
about it (not necessarily mine):

* Function types would introduce a form of structural typing, which
is a little alien to Java. We saw a thread here just a few days
ago ("simple method to simulate function pointers in java"), where
someone was hand-crafting generic function types, and some of the
advice was just to define interfaces per situation, partly because
it documents better, and partly because it's more idiomatic for Java.
* Function types could be added in such a way that they would be SAM
types, so if you can get lambdas to work with SAM types (which
you'd probably have to do anyway to take advantage of old APIs),
they should automatically work with function types added later.
* A limited number of generic SAM types are being defined to extend
the Collections API. They will probably serve as well as function
types in the most common cases.
* If a lambda is always typed as a SAM, how you invoke it is settled
- it's the name of the SAM's method, of course. I recall seeing
proposals where you could write obj(args), or obj.(args), or
obj.invoke(args), where obj was a function type. Leave it as a
SAM, and you don't have to make a decision about that.

You say 'yet' - can we expect function types before it's finished?

IIRC, no plans for Java 8 - back burner, I think. ISTR some
difficulties arose, so maybe they decided they needed more time for
something deemed not essential.
 
B

BGB

Hmm, it's more strict than I thought (but easier to compile). It seems
you have to be able to add final to the variable declaration. This
compiles fine:

void func() {
int i = 0;

Runnable action = () -> { System.out.println(i); };
}

...but any assignment to 'i', before or after creating 'action',
prevents 'i' from being effectively final.

Hardly a disaster. :)

I would have also liked to see lexical variable capture.
FFS, I added this (along with closures) to a C compiler before, can't be
too hard (never-mind the eventual fate of said C compiler, but alas, a
sad example of slow compile times and my apparently inability to
effectively debug it...).


more interestingly, they would look just like normal C function
pointers, and survive past the end of the parent scope (unlike, say,
their C++0x analogues, which apparently follow the much weaker/lamer
semantics of GCC's nested functions...).


in the case of lexically captured variables (both captured and mutable),
an implicit heap-based object could be created, representing any such
captured variables.

so:
void func()
{
int i=0;
Runnable fcn = ()=>{ i++; };
... do stuff with fcn ...
System.out.println(i);
}

the 'i' above could be itself folded into a class (any references to 'i'
would themselves implicitly be directed through this class).

sort of like (it compiled to):
void func()
{
private class __cap { public int i=0; } //invisible
__cap _cap=new __cap(); //invisible

Runnable fcn = ()=>{ _cap.i++; };
... do stuff with fcn ...
System.out.println(_cap.i);
}

now, assuming that the variable is effectively final, none of the above
is done, and the variable is copied by value (thus avoiding the cost of
the additional object).

Some corner cases came up, along with some work-arounds. They were
probably recursive. I think assignment rules were altered slightly so
that a lambda body could refer to itself:

Runnable x = () -> { x.run(); };

It was deemed okay to allow this because you can be certain that x will
be ready before it is run.

yep.

still idly thinking here of syntax sugar for SAM types.

maybe even abusing an existing keyword:
public interface void SomeFunc(int x);


implicitly is (more or less) the equivalent of:
public interface SomeFunc
{
public void run(int x);
}

and maybe with a little syntax sugar also on the caller end:

SomeFunc func;
....
func(i); //internally: func.run(i);
....

granted, yes, it is all syntax sugar at this point.

Yes, except I'm not sure how useful it will be in practice with
concurrent APIs. With a serial contract, like a library version of the
for-each loop, it's clearly useful:

interface Block<E, throws T> {
void apply(E val) throws T;
}

static<E, throws T> void forEach(Iterable<E> coll, Block<E, T> block)
throws T {
for (Iterator<E> iter = coll.iterator(); iter.hasNext(); ) {
E elem = iter.next();
block.apply(elem);
}
}

Now the forEach method can generically throw the same set of exceptions
T that the block itself can throw.

But if there is no serial guarantee on invoking the block, you have to
choose whether you return arbitrarily just the first exception thrown,
or pack them into some structure, or something else. I've a feeling
that, most of the time, such methods will require the SAM type to throw


I think they don't foresee such savings for anon classes generally,
because they potentially have fields and multiple user-defined methods.
When you're certain such complexities don't exist, which is the case for
lambdas, certain optimizations become possible.

yes, optimize the special cases, this is generally how it works...

if one tries to optimize for the general cases, then one almost
invariably ends up with much added complexity and generally poor results.

potentially in cases where there is only a single method and no state
capture, ... the VM could potentially internally decay it into being
essentially a plain function pointer or similar (eliminating any class
or instances thereof).

less aggressive would be to only ever create a single instance, and all
references are to this, and probably some special-case call optimization.

You seem disturbingly excited by that! :) Here are some arguments about
it (not necessarily mine):

* Function types would introduce a form of structural typing, which
is a little alien to Java. We saw a thread here just a few days
ago ("simple method to simulate function pointers in java"), where
someone was hand-crafting generic function types, and some of the
advice was just to define interfaces per situation, partly because
it documents better, and partly because it's more idiomatic for Java.
* Function types could be added in such a way that they would be SAM
types, so if you can get lambdas to work with SAM types (which
you'd probably have to do anyway to take advantage of old APIs),
they should automatically work with function types added later.
* A limited number of generic SAM types are being defined to extend
the Collections API. They will probably serve as well as function
types in the most common cases.
* If a lambda is always typed as a SAM, how you invoke it is settled
- it's the name of the SAM's method, of course. I recall seeing
proposals where you could write obj(args), or obj.(args), or
obj.invoke(args), where obj was a function type. Leave it as a
SAM, and you don't have to make a decision about that.

lame, IMO...

having spent more of my time using languages with much nicer first-class
functions (yes, I will include C here, as well as JavaScript and
similar...), having to have extra syntax (both to declare and use these
types) is IMO lame.

in all cases, one would type "obj(args)".

idiomatic for Java or not, the more compact declarations and invocations
are more what people who use most other languages are likely to expect
(and ideally the compiler can be smart enough to figure out what
"obj(args)" with a SAM means).


although I kind of doubt this:
has anyone also considered the ability to grab existing methods from
objects/classes, and assign them to new method-fields?... (probably
restricted to a single parent class).

this can be nifty for some types of programs (mostly games, IME), as
then one can more easily have context-dependent methods.

C# also has something like this.

IIRC, no plans for Java 8 - back burner, I think. ISTR some difficulties
arose, so maybe they decided they needed more time for something deemed
not essential.

yep...


this is partly why I continued investing time/effort/... into my own VM
and language-design efforts. mature languages are slow-moving targets,
and so will not be like what oneself may want them to be "anytime soon".

a person may be then better off throwing together their own custom
language with the features they want, and mostly leave the people
working on more mature technology to do what they do well: keep the
thing as a reasonably "solid" piece of technology, as something which
changes too much too quickly may be perceived as being a bit flaky (and
this may actually be the case, if the addition of new features outpaces
things like optimizing and debugging them).

and, at the same time, the person who wants a more specialized language,
can have this as well...

the world isn't perfect but it is generally good enough...
 
S

Steven Simpson

I would have also liked to see lexical variable capture.
FFS, I added this (along with closures) to a C compiler before, can't
be too hard

At this stage, I don't think the issue is how, but whether/when to
permit it.

All suggestions for how seem to come down to boxing:

* arrays of length 1
* hidden local classes
* AtomicInteger, etc

(If there were any others, I didn't understand them.)

The options for when/whether have been:

* never
* always
* when a local is tagged with @Shared, public or similar
* when the SAM parameter is tagged with @Callback, @Block or similar

They've gone for 'never' because it's sufficient to meet their primary
goals of supporting concurrent APIs, especially on collections, and they
want to encourage good concurrent practices. They can still look at the
other options later - it would be harder to withdraw an advanced
feature, having discovered it was a bad idea.
having spent more of my time using languages with much nicer
first-class functions (yes, I will include C here, as well as
JavaScript and similar...), having to have extra syntax (both to
declare and use these types) is IMO lame.

in all cases, one would type "obj(args)".

idiomatic for Java or not, the more compact declarations and
invocations are more what people who use most other languages are
likely to expect (and ideally the compiler can be smart enough to
figure out what "obj(args)" with a SAM means).

Lambda declarations are already quite compact, with type inference
helping in most cases.

new Thread(() -> { doSomething(); }).start(); // Runnable implied

For invocations, having to type obj.run() instead of obj() is hardly
onerous. Plus, invocations will be much rarer than lambda
declarations. Also note that the invocation site is unaware of whether
the object is a lambda.
 
B

BGB

At this stage, I don't think the issue is how, but whether/when to
permit it.

All suggestions for how seem to come down to boxing:

* arrays of length 1
* hidden local classes
* AtomicInteger, etc

(If there were any others, I didn't understand them.)

The options for when/whether have been:

* never
* always
* when a local is tagged with @Shared, public or similar
* when the SAM parameter is tagged with @Callback, @Block or similar

They've gone for 'never' because it's sufficient to meet their primary
goals of supporting concurrent APIs, especially on collections, and they
want to encourage good concurrent practices. They can still look at the
other options later - it would be harder to withdraw an advanced
feature, having discovered it was a bad idea.

fair enough, just I guess this makes it a bit different from several
other languages with closures (such as JS).

Lambda declarations are already quite compact, with type inference
helping in most cases.

new Thread(() -> { doSomething(); }).start(); // Runnable implied

For invocations, having to type obj.run() instead of obj() is hardly
onerous. Plus, invocations will be much rarer than lambda declarations.
Also note that the invocation site is unaware of whether the object is a
lambda.

yes, ok.


however... whether or not it is a lambda could be made a side issue:
Runnable obj;
....
obj();

could be made to "just work" (with either a lambda, or with an interface).


it may not matter much for things like traditional callbacks/..., but it
could make more of a difference if people want something like assignable
methods.

being able to type: "obj.someAssignableMethod();" would be a little
nicer looking than "obj.someAssignableMethod.run();".

even if, yes, this is all basically just syntax sugar.
(among other things, like getter/setter properties, ...).



also, misc:
in my own language, the above thread example could be written:
async { doSomething(); }

( I had considered making the braces optional, but at the moment this
poses more subtle issues. in the above context, async serves as a
thread-creation keyword. )


not that I think syntax sugar is a huge issue though, as (after all) I
still do much of my programming in C (peoples' value-judgements against
C aside...).


or such...
 
J

Joshua Cranmer

At this stage, I don't think the issue is how, but whether/when to
permit it.

There are other issues like does it capture the value or does it use the
same variable. e.g., what would this produce:

List<Runnable> runners = new LinkedList<Runnable>();
for (int i = 0; i < 10; i++) {
runners.add(() => { System.out.println("Value of i is " + i); });
}
for (Runnable r : runners) {
r.run();
}

Should you see 0..9 or 10 repeated 10 times?
For invocations, having to type obj.run() instead of obj() is hardly
onerous. Plus, invocations will be much rarer than lambda declarations.
Also note that the invocation site is unaware of whether the object is a
lambda.

Also, note the (slight) benefits of explicitly saying what you are
doing. You might choose, reasonably, to call the callback parameter for
an asyncForEach function `block', at which point the functional call
specification becomes block(value), which can be visually ambiguous as
to what it's doing. block.call(value) is clearer, on the other hand.
 
B

BGB

There are other issues like does it capture the value or does it use the
same variable. e.g., what would this produce:

List<Runnable> runners = new LinkedList<Runnable>();
for (int i = 0; i < 10; i++) {
runners.add(() => { System.out.println("Value of i is " + i); });
}
for (Runnable r : runners) {
r.run();
}

Should you see 0..9 or 10 repeated 10 times?

most languages I am aware of with closures (and mutable state) capture
the variable itself, so one would see 10 repreated 10 times (since the
original variable now holds 10).


in a different context, I had run into this issue, and added a special
form to the block (theoretically, IIRC not yet implemented) to
explicitly capture the state of the variable at that point (rather than
a reference to this variable). interestingly, this internally converted
into a closure which accepted the variables as arguments and was then
called with these variables.

a more generalized form of this would look something like:
for(i=0; i<10; i++)
begin(i) {
...
}
....

with "begin(i) { ... }" basically meaning to execute '...' with 'i'
having been captured (by value).

this could also be user like "begin(i, j) {...}" to capture two values,
or "begin(i, j=i*251) {...}" to capture the value of i and bind j as a
computed value (sort of like "(let)" and friends in Lisp and Scheme).

however, I have doubts that such a feature would map nearly so cleanly
to Java or the JVM.


in C++0x, the type of variable capture was made explicit in the lambda
syntax:
"[](...) {...}" (no capture allowed)
vs
"[&](...) {...}" (capture by reference)
vs
"[=](...) {...}" (capture by value).
vs
more complex forms...


Also, note the (slight) benefits of explicitly saying what you are
doing. You might choose, reasonably, to call the callback parameter for
an asyncForEach function `block', at which point the functional call
specification becomes block(value), which can be visually ambiguous as
to what it's doing. block.call(value) is clearer, on the other hand.

yes, but I guess it depends some on what one is doing, and whether or
not it is better to complicate some potential use cases for sake of
preventing people from shooting themselves in the foot in others (or,
OTOH, gloss over certain complexities at the risk of people then
shooting themselves in the foot...).


or such...
 
D

Daniele Futtorovic

List<Runnable> runners = new LinkedList<Runnable>();
for (int i = 0; i < 10; i++) {
runners.add(() => { System.out.println("Value of i is " + i); });
}
for (Runnable r : runners) {
r.run();
}

Beg your pardon, I haven't followed this issue as closely as I probably
have should, but does this all mean lambdas are always Runnables? Never
Callable<T>s? IOW, no return values?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top