new Java lambda syntax

Discussion in 'Java' started by markspace, Sep 8, 2011.

  1. markspace

    markspace Guest

    Just thought some folks might be interested in this:


    Date: Thu, 08 Sep 2011 16:07:03 -0400
    From: Brian Goetz <>
    To: "" <>


    "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."
     
    markspace, Sep 8, 2011
    #1
    1. Advertising

  2. markspace

    Roedy Green Guest

    On Thu, 08 Sep 2011 13:19:22 -0700, markspace <-@.> wrote, quoted or
    indirectly quoted someone who said :


    > 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)
     
    Roedy Green, Sep 8, 2011
    #2
    1. Advertising

  3. markspace

    Arne Vajhøj Guest

    On 9/8/2011 6:18 PM, Roedy Green wrote:
    > On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    > indirectly quoted someone who said :
    >
    >
    >> 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.


    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
     
    Arne Vajhøj, Sep 8, 2011
    #3
  4. markspace

    BGB Guest

    On 9/8/2011 3:18 PM, Roedy Green wrote:
    > On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    > indirectly quoted someone who said :
    >
    >
    >> 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.
    >


    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...
     
    BGB, Sep 8, 2011
    #4
  5. markspace

    Arne Vajhøj Guest

    On 9/8/2011 6:40 PM, BGB wrote:
    > On 9/8/2011 3:18 PM, Roedy Green wrote:
    >> On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    >> indirectly quoted someone who said :
    >>
    >>
    >>> 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.
    >>

    >
    > 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
     
    Arne Vajhøj, Sep 9, 2011
    #5
  6. markspace

    BGB Guest

    On 9/8/2011 4:27 PM, Arne Vajhøj wrote:
    > On 9/8/2011 6:40 PM, BGB wrote:
    >> On 9/8/2011 3:18 PM, Roedy Green wrote:
    >>> On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    >>> indirectly quoted someone who said :
    >>>
    >>>
    >>>> 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.
    >>>

    >>
    >> 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; }
    >


    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...
     
    BGB, Sep 9, 2011
    #6
  7. On 9/8/2011 3:18 PM, Roedy Green wrote:
    > On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    > indirectly quoted someone who said :
    >
    >
    >> 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.
    >


    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
    <____ ____> Waterloo Maple Inc.
    | Type ? for help.

    > foo:=x->x^2;

    2
    foo := x -> x

    > foo(10);

    100


    --Nasser
     
    Nasser M. Abbasi, Sep 9, 2011
    #7
  8. markspace

    Arne Vajhøj Guest

    On 9/8/2011 7:29 PM, BGB wrote:
    > On 9/8/2011 4:27 PM, Arne Vajhøj wrote:
    >> On 9/8/2011 6:40 PM, BGB wrote:
    >>> On 9/8/2011 3:18 PM, Roedy Green wrote:
    >>>> On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    >>>> indirectly quoted someone who said :
    >>>>
    >>>>
    >>>>> 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.
    >>>>
    >>>
    >>> 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; }
    >>

    >
    > 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
     
    Arne Vajhøj, Sep 9, 2011
    #8
  9. On 9/8/2011 5:18 PM, Roedy Green wrote:
    > On Thu, 08 Sep 2011 13:19:22 -0700, markspace<-@.> wrote, quoted or
    > indirectly quoted someone who said :
    >
    >
    >> x => x + 1
    >> (x) => x + 1

    >
    > 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.

    --
    Beware of bugs in the above code; I have only proved it correct, not
    tried it. -- Donald E. Knuth
     
    Joshua Cranmer, Sep 9, 2011
    #9
  10. markspace

    BGB Guest

    On 9/8/2011 5:56 PM, Peter Duniho wrote:
    > On 9/8/11 4:29 PM, BGB wrote:
    >> [...]
    >>> 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...


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

    >
    > 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...
     
    BGB, Sep 9, 2011
    #10
  11. markspace

    Tom Anderson Guest

    On Thu, 8 Sep 2011, markspace wrote:

    > "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

    --
    The ultimate crime-based society is one where everyone is criminal and
    no one is aware of the fact. -- J. G. Ballard, Cocaine Nights
     
    Tom Anderson, Sep 10, 2011
    #11
  12. On 10/09/11 14:48, Tom Anderson wrote:
    > 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.

    --
    ss at comp dot lancs dot ac dot uk
     
    Steven Simpson, Sep 10, 2011
    #12
  13. markspace

    Tom Anderson Guest

    On Sat, 10 Sep 2011, Steven Simpson wrote:

    > On 10/09/11 14:48, Tom Anderson wrote:
    >
    >> 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.


    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.

    >> 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.


    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

    --
    the themes of time-travel, dreams, madness, and destiny are inextricably
    confused
     
    Tom Anderson, Sep 11, 2011
    #13
  14. On 11/09/11 13:06, Tom Anderson wrote:
    > On Sat, 10 Sep 2011, Steven Simpson wrote:
    >> * '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?


    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. :)

    >> * '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?


    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.

    >> * '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.


    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, so <throws T> won't get used.

    >> 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.


    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.

    >> 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 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.

    --
    ss at comp dot lancs dot ac dot uk
     
    Steven Simpson, Sep 11, 2011
    #14
  15. markspace

    BGB Guest

    On 9/11/2011 9:18 AM, Steven Simpson wrote:
    > On 11/09/11 13:06, Tom Anderson wrote:
    >> On Sat, 10 Sep 2011, Steven Simpson wrote:
    >>> * '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?

    >
    > 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).


    >>> * '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?

    >
    > 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.


    >>> * '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.

    >
    > 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, so <throws T> won't get used.
    >
    >>> 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.

    >
    > 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.


    >>> 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 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.


    >
    >> 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.
    >


    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...
     
    BGB, Sep 11, 2011
    #15
  16. On 11/09/11 19:08, BGB wrote:
    > 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.

    --
    ss at comp dot lancs dot ac dot uk
     
    Steven Simpson, Sep 11, 2011
    #16
  17. markspace

    BGB Guest

    On 9/11/2011 12:14 PM, Steven Simpson wrote:
    > On 11/09/11 19:08, BGB wrote:
    >> 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.
    >


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


    >> 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.
    >


    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...
     
    BGB, Sep 11, 2011
    #17
  18. On 9/11/2011 2:14 PM, Steven Simpson wrote:
    > On 11/09/11 19:08, BGB wrote:
    >> 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.


    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.

    --
    Beware of bugs in the above code; I have only proved it correct, not
    tried it. -- Donald E. Knuth
     
    Joshua Cranmer, Sep 11, 2011
    #18
  19. markspace

    BGB Guest

    On 9/11/2011 3:07 PM, Joshua Cranmer wrote:
    > On 9/11/2011 2:14 PM, Steven Simpson wrote:
    >> On 11/09/11 19:08, BGB wrote:
    >>> 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.

    >
    > 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...



    >> 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.
    >


    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...
     
    BGB, Sep 12, 2011
    #19
  20. On 12/09/2011 00:07, Joshua Cranmer allegedly wrote:
    >
    > 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?

    --
    DF.
    Determinism trumps correctness.
     
    Daniele Futtorovic, Sep 13, 2011
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roman Suzi
    Replies:
    13
    Views:
    620
    Bengt Richter
    Jan 7, 2005
  2. Beni Cherniavsky

    Special keyword argument lambda syntax

    Beni Cherniavsky, Mar 13, 2009, in forum: Python
    Replies:
    1
    Views:
    328
    Beni Cherniavsky
    Mar 14, 2009
  3. Farrel Lifson

    1.9 -> lambda syntax with &

    Farrel Lifson, Nov 18, 2008, in forum: Ruby
    Replies:
    1
    Views:
    178
    Brian Candler
    Nov 18, 2008
  4. Steve Dogers

    lambda vs non-lambda proc

    Steve Dogers, Mar 30, 2009, in forum: Ruby
    Replies:
    1
    Views:
    194
    Sean O'Halpin
    Mar 30, 2009
  5. Haochen Xie
    Replies:
    4
    Views:
    258
    Haochen Xie
    Mar 17, 2013
Loading...

Share This Page