LJC Lambdas Hackday

Richard Warburton richard.warburton at gmail.com
Tue May 29 06:30:02 PDT 2012


On Sunday 27th May, the London Java Community ran a lambdas hackday as
part of the Adopt OpenJDK program.  We had a mixture of different
abilities and experiences, a couple of people had written some Scala,
Clojure or Haskell before, but most people were day-to-day Java
developers without that background.

We split people into groups of 3 or 4, gave them the lambdafied Java 8
build (thanks Brian & team!) and a little bit of understanding to
begin with. They then wrote a few simple Collections based programs in
the morning, and tried to lambda-ise parts of the Cassandra Database
in the afternoon.  Here’s a summary of what we learnt.

Bugs

1. Target typing for diamond operators being passed into methods
doesn’t seem to work quite right at the moment.  This compiles ok:

 public static void main(String[] args) {
       List<String> appended = Arrays.asList(args)
           .map(x -> x + "suffix")
           .into(new ArrayList<String>());
       System.out.println(appended);
   }

If we compile:

  public static void main(String[] args) {
       List<String> appended = Arrays.asList(args)
           .map(x -> x + "suffix")
           .into(new ArrayList<>());
       System.out.println(appended);
   }

Then we get a compile error:

Foo.java:8: error: incompatible types
           .into(new ArrayList<>());
                ^
 required: List<String>
 found: ArrayList<Object>
1 error

2. The state of the lambda collections libraries document lists
several methods that haven’t been implemented yet, for example sum()
and mapBy().  On a side note - sum() seems like an interesting method,
what is its type?

There’s another bug around generics that Ben will be emailing through,
when he has a extracted a minimal testcase.

Common Mistakes

Several different people made similar mistakes when trying to write
simple lambda based Java 8 code. I don’t think these are necessarily
design weaknesses, but they are things that need to be thought through
in terms of how you educate people about lambdas and how you promote
lambdas.

1.  People thinking that map/reduce return an eagerly evaluated List,
rather than a lazily evaluated Iterable. This was especially confusing
when we discussed the sorted method, and whether calling getFirst() on
a lazy iterator ends up generating any iterators that you’ve
dependently chained. A brief scan of the source shows you’re using a
priority queue, but this is the kind of thing that people will get
confused at when you’re now talking about a lot of their collections
operations being lazy.

I don’t think anyone disagreed with the core idea that you chain a
sequence of lazy operations, and then had an eager evaluation at the
end. At least no one had a compelling use case that this didn’t work
with. I think it will take time for people to become used to and
comfortable with this idea.

2. Some programmers appear to have basically no functional programming
experience and were confused as to why they couldn’t assign back to
their parameter in a map.  For example they wanted to write:

someIntegers.forEach(x -> x = x + 1)

rather than:

someIntegers.map(x -> x + 1)

I know this might sound silly to people subscribed to lambda-dev, but
getting people out of a solely imperative mindset of one of the
adoption challenges that is faced by this JSR.  If I may be so bold as
to make a suggestion in order to help people in the right direction:
lambda arguments being default final.  (Or maybe even always final)

3. Expressions that evaluate to void vs code blocks.

“someStrings.forEach(s -> System.out.println(s));”

doesn’t compile because the function call is an expression that
evaluates to void, so it's incompatible with Block<String>.

“someStrings.forEach(s -> { System.out.println(s); });”

works fine because the braces with no return allow the user to
convince javac that it's returning a void.

Two different groups of 4 people and at least one of the instructors
found this quite confusing.  In many functional languages, with the
equivalent of void (Unit, etc) being a normal type, the first
statement would have type checked.

Opinions

These are opinions that I sourced from people whilst they were using
the lambdas build.  All of them were supported by at least 2 people
and many of them were common threads when talking to different groups
of programmers.

1. Javadoc needs examples.  For a lot of Java developers they’ll have
a problem and need to figure out which higher order function is
appropriate for their use case. For example they have a list of
numbers that they wish to increment, but they’ll need to figure out
whether they want to use map or reduce.  Again - seems trivial to
people with some experience but many won’t have that experience. In
this case the style of Javadoc - ie explain what’s happening - that’s
throughout Collections isn’t necessarily that appropriate.  It would
be much more helpful to have a simple code example in order for people
to grok the pattern that they need to apply.  E.g. Incrementing
numbers for map, concatenating a list of strings for reduce etc.  It
would also help people coming from other backgrounds if methods listed
similar method names.

For example you could say that the method ‘reduce’ might be called
‘fold’ in other languages.  I appreciate this isn’t /technically/
correct - but it helps people build an intuitive understanding of
what’s going on here and helps get them up to speed quickly.  Just
don’t say that they’re catamorphisms or people will try to google
catamorphism and quickly become confused ;)

2. Complex types are hard to read and understand.  Scala already gets
a really bad rep for this, and Java will as well if you’re not
careful.  Consider the function map’s signature:

public static <T, U> Iterable<U> map(final Iterable<? extends T>
iterable, final Mapper<? super T, ? extends U> mapper)

There are a couple of things that you can do on a practical level to
help people understand this better.

a. Write Javadoc in a way that discourages people from reading the
types in order to figure out what’s going on. So, if the javadoc
explains what a mapper is, and what the Iterable<U> is that’s returned
then you don’t need to understand the types at all.

b. Use sensible type parameter names.  The basic fact is that generic
parameters are variables - they simply range over sets of types,
rather than sets of values - so why not apply descriptive rules for
variable names as you would normal variables.  For example:

public static <From, To> Iterable<To> map(final Iterable<? extends
From> iterable, final Mapper<? super From, ? extends To> mapper)

Now you don’t get people looking at the type signature and immediately
asking themselves - what does ‘U’ mean?

3.  Since other type quantifiers like final and public are on the left
hand side of a method, perhaps default should go there as well?  I’m
not really a big fan of syntax discussion in general, but this was the
one syntactic point that was brought up that I don’t think was
discussed here previously.

4. When trying to lambda-ise cassandra it became clear that frequently
you have a list that has multiple responsibilities.  If you were to
lambda-ise it, then you’d split things out into multiple calls and
chain them together.  This means that in many cases there will be some
refactoring needed in order to introduce lambda usage.  Some people
commented that in their workplace the risk of changing reliably
working code outweighs the potential benefits that you’re providing.
Perhaps there’s no good way to address this issue - I certainly have
no suggestions other than getting people to write clean code with high
test coverage so they can easily refactor.

5. Some anonymous inner classes ended up with a hell of a lot of
parameters (e.g. 7) being passed in. In this case people seemed to
prefer to continue using anonymous inner classes, rather than
splitting up the parameter declaration of a lambda expression over
several lines.

Future Directions

When trying to lambda-ise cassandra we ran into several Apache Ant
bugs that we established temporary workarounds for.  I think before we
run an event like this again we’ll want to patch Ant so that it works
ok.  Do you guys have internal builds of ant that work ok with Java 8?
 If you don’t, I’m sure we’re more than capable of fixing the problem,
but its still easier if we don’t have to!

regards,

 Richard, Ben and Martijn


More information about the lambda-dev mailing list