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

Stephen Colebourne scolebourne at joda.org
Thu Jun 16 03:03:26 PDT 2011


I believe I agree with Steven and Reinier that the strawman syntax
family would be best with only one paren for expressions:

 process( #(x) x + 1 )
 process( #(x) { return x + 1; } );
 process( #(x) {
   if (x == 0) {
     return 0;
   }
   return x + 1;
 });

The one less paren helps me read the code, compared to this:

 process( #(x) (x + 1) )

(Of course, users might choose to add the parens themselves, as thats
legal for any expression)


I have mixed feelings on supporting a lone expression within the
block. Thats because I'm unconvinced that the equivalent for a method
is as readable as using a "return":

process( #(x) {x + 1} );   // reads well enough to me
public String getSurname() {surname}  // doesn't read quite as well to me


For other syntax families, I also suspect that I prefer that the
expression itself does not require being surrounded by parens.

Stephen


On 16 June 2011 07:55, Reinier Zwitserloot <reinier at zwitserloot.com> wrote:
> 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