Optional brackets around lambda expression (was: Expected distribution of lambda sizes)

Reinier Zwitserloot reinier at zwitserloot.com
Wed Jun 15 23:55:05 PDT 2011


My gut instinct was: That's a really bad idea. But I decided to play around
with it anyway and it actually resulted in easier to scan code (to me,
anyway). The use cases I've worked on before indeed usually boil down to
passing the closure straight into a method as an argument, and in such cases
it's better. As Pavel said, if you treat the closure preamble (the => or the
#() or whatever the syntax calls for) as an operator with low precedence, it
works out.

This way the strawman syntax almost seems to have that best of both worlds
scenario: It looks good both for long and one-liner closures. Passing
closures inline as argument to a method, i.e. the map/filter usecase, looks
a lot cleaner when you lose a closing brace/paren.

Precedence-wise, if you want to exit the closure scope, you have to resort
to this:

int ageOfJoe = (#(Person x) x.getAge()).invoke(joe);

which is different from what traditional strawman wanted you to do:

int ageOfJoe = #(Person x) (x.getAge()).invoke(joe);

Personally I found it easier to scan the new form vs. the old one (i.e. its
easier to see in the top version that the toString applies just as much to
the #(Person x) part as the x.getAge() part), so this is not necessarily a
bad thing.

I don't think its necessary but this proposal can be extended by allowing
braces for single expressions, i.e:

int ageOfJoe = #(Person x) {x.getAge()}.invoke(joe);

 though I'm not sure how easy it is to implement this in an LL(k) parser
(you'd have to conflate the first statement/expression as 'its one of those
two', then scan for either a semicolon or the closing brace, at which point
you have to go back and doublecheck that the statement or expression you
parsed is actually an expression if there wasn't one, or actually a
statement if there was. That's different than what javac currently does; it
always knows if it wants a statement or an expression, it never needs to
parse an 'it's one of those'. ecj will have any problem with this as it
already junks statements and expressions into the same pile.


 --Reinier Zwitserloot



On Wed, Jun 15, 2011 at 10:42 PM, Pavel Minaev <int19h at gmail.com> wrote:

> Note that this applies also to "Redmond syntax" in general; e.g.:
>
>   new User().use(x => x + "a string");
>
> However, I do not recall it ever being a problem in C# in practice, nor did
> I ever hear of complaints about that in C#. In general, lambda syntax is
> treated as a sort of "lambda operator" =>, with a certain "precedence", and
> said "precedence" is lower than any other operator - in this case, lower
> than "+". In that sense, it's not much different than correctly parsing
> expressions such as a || b && c.
>
> On Wed, Jun 15, 2011 at 1:35 PM, Yuval Shavit <yshavit at akiban.com> wrote:
>
> > What would happen with something like:
> >
> >  public interface Sam {
> >    String doSomething(String arg);
> >  }
> >  public class User {
> >    public void use(Sam sam) { System.out.println("saw a sam"); }
> >    public void use(String string) { System.out.println("saw a string"); }
> >  }
> >
> >  new User().use( #(x) x + "a string" );
> >
> > In that context, which are we passing?
> >   - lambda that takes a String, concatenates "a string" to it and returns
> > the result
> >   - a string consisting of (lambda x -> x).toString() concatenated with
> "a
> > string"
> >
> > This is just one simple and somewhat contrived example, but the point is
> > that without braces or something similar, it's not too hard to come up
> with
> > a situation where it's not clear where the lambda ends and the rest of
> the
> > expression begins.
> >
> > On Wed, Jun 15, 2011 at 4:18 PM, Steven Simpson <ss at comp.lancs.ac.uk>
> > wrote:
> >
> > > On 13/06/11 21:10, Pavel Minaev wrote:
> > > > Statement lambdas would typically be thus wrapped, yes
> (coincidentally,
> > > it's
> > > > also why I'd prefer to have a separate form for expression lambdas
> > which
> > > > does not include the {} so as to be visually distinct from statement
> > > > blocks).
> > >
> > > Yes, I think I'd appreciate that visual distinction too.  Is it
> possible
> > > to have an unambiguous expression syntax not requiring any form of
> > > brackets around either the whole lambda or its body?:
> > >
> > >  #() 3
> > >  #() 3 + 4
> > >  #(x) x + 1
> > >  #(x, y) x + y
> > >
> > > When used like this, they will likely be in argument lists, so they are
> > > naturally delimited by commas and the list-terminating bracket, and
> need
> > > no brackets of their own.  IOW, #() would have quite a low precedence,
> > > and any brackets put around it would be part of existing syntax (e.g.
> > > for normal expressions and argument lists).
> > >
> > > If you had one lambda in the body of another, it will likely have to be
> > > inside an enclosed call, whose brackets will naturally delimit it:
> > >
> > >  #(a) a + process(#(b) b * b) + 10
> > >
> > > If it wasn't so embedded, you could always put normal expression
> > > brackets around it:
> > >
> > >  #(a) a + (#(b) b * b).invoke(a)
> > >
> > > (And that's a little contrived.  And possibly quite difficult to
> > > type-infer…?)
> > >
> > > I don't think you could drop the parameter-list brackets too, as that
> > > would make parsing much more complex (e.g. #a + a).  Looking at that
> > > more positively, it would keep lambda syntax and appearance obviously
> > > distinct from method literals.  ("#(" => Bam!  I'm a lambda!)  The
> > > presence of braces would then further distinguish lambda expressions
> > > from lambda statements.
> > >
> > > In summary, we require brackets around neither the lambda expression
> nor
> > > its body.  We are then forced to used some around the parameter list,
> > > but take advantage of this as the constant discriminant of lambda vs
> > > method literal.
> > >
> > > Applied to some other expressions seen on the list lately:
> > >
> > >   list.filter( #(t) t.length() > 3 )
> > >       .map( #(t) t.barCount )
> > >       .max();
> > >
> > >   students.filter(#(s) s!=null && "Smith".equals(s.getName()))
> > >
> > >   List men = personList.filter(#(p) p.isMale());
> > >
> > >   List<Double> adjustedPrices =
> > >      prices.map(#(price) price + getShipping());
> > >
> > > Cheers,
> > >
> > > Steven
> > >
> > >
> > >
> >
> >
>
>


More information about the lambda-dev mailing list