London Lambdas Hackday: Existing Code
Richard Warburton
richard.warburton at gmail.com
Tue Jul 3 04:43:26 PDT 2012
Some thoughts from lambdafying existing code.
Example A: Vert.x
One of the projects we tried to lambda-ize was vert.x. This is
essentially an asynchronous eventing system (similar to node.js) on
the JVM. This was a big success in the sense that because everything
was based upon callbacks to a handler interface, it was incredibly
straightforward to lambda-ize and it made code both easier to read and
shorter. Unfortunately it was really too easy - we didn't really
learn that much from this exercise.
My only comment building on this is that variable scoping makes
converting anonymous inner classes more difficult than absolutely
necessary. The context to this was a programmer converting anonymous
inner classes in some existing code to lambdas in order to remove
boilerplate. Due to the fact that the inner function had a variable
with the same name as a variable that had been defined in the outer
method, he received a “error: variable num is already defined in
method” message.
Example B: MutabilityDetector
Firstly thanks for this specifically go to Graham Allen and his
interesting project MutabilityDetector[0], which brings up an
interesting example of how some code might evolve into lambdas. Its
also an interesting example because its a slightly leftfield use of
collections. Take a look at the following three variations of the
same class.
Plain java implementation, no guava and no lambdas [1]
(note the use of variable names to describe the transformations that
were occuring)
With Guava functions, and using anonymous inner classes [2]
The jdk8 lambda implementation, using jdk8 collections [3]
(note I could have inlined, but wanted to keep the naming. Also now
happy to make the method static, so there's no public static instance
there)
This example spurned some interesting discussion:
1. Guava’s predicates vs Java 8 Predicates
People using Guava's predicate library already will have an interface
doing something identical to the Java 8 Predicate. Furthermore they
will be able to
2. It's a win
There was a large code reduction and readability improvement over the
legacy code, both plain Java and the guavised code.
3. Method References
If you heavily use guava's FP constructs then there's an expectation
of needing some kind of Function<Foo,Bar>. In other words if you're
using a method reference SomeClass::someMethod, then you expect
someMethod to be of type, "Function<Foo,Bar> someMethod()" rather than
"Foo someMethod(Bar bar)". From talking to people, it actually seems
like method references are more natural if they're not coming from a
guava background. This isn't just guava - there are several libraries
offering a limited range of FP Oriented facilities. Not sure what can
be done about this, but perhaps this is another thing that could be
helped with documentation and examples, rather than needing a
language/API change.
On a related note, it was initially hard to understand where you got a
method reference from. I suspect that this is something that
programmers need to just develop a sense for, but there certainly was
confusion about static and instance method references. Some people
tried to use SomeClass::someMethod instead of someInstance::someMethod
when getting obtaining references. At a conceptual level it seems
obvious if you're referring to an instance method it needs to be
associated with some instance, but there is something confusing about
this.
4. Factory naming conventions don't make sense anymore
If you care about API Fluency then you might frequently have factory
methods that are named in that style, for example:
LocalDate eventDate = LocalDate.from(eventTimestamp)
And "LocalDate from eventTimestamp" reads like an English sentence.
However when you start to use a factory method as a method reference,
the naming convention is totally lost.
List<LocalDate> events = timestamps.map(Localdate::from)
.into(new ArrayList<LocalDate>())
Here "timestamps map Localdate from" doesn't read as an English
sentence. What I really want to say in code is the equivalent of
"timestamps map into LocalDate". Some attempts:
(a) Now if you rename the factory method "from" into "into", then you
get "timestamps map Localdate into", which is better, but this then
breaks the factory naming convention for non-mapping examples, so
"LocalDate.into(timestamp)" reads the wrong way round.
(b) If you invert the class and method order in method references then
you end up with "timestamps map into LocalDate", but obviously this
has broken the connection between SomeClass.someMethod and
SomeClass::someMethod that method references are premised upon. Which
I'm guessing pretty much rules it out.
(c) You can write a second factory method that uses a functional
interface, and then static import it, for example in the library:
public static final Mapper<Long, LocalDate> intoLocalDate = LocalDate::from
And in the client code, use a static import:
timestamps.map(intoLocalDate)
This still fails if the factory method is overloaded by argument, as
LocalDate.from is, since you can't overload the field "intoLocalDate"
by type. You can get around this by having multiple factory methods
for each argument type, eg:
public static final Mapper<Long, LocalDate> intoLocalDateByTimestamp =
LocalDate::from
This still isn't nice, because it requires that the library author
writes a duplicate for all their fluent factory methods if they expect
them to be used as a mapper, which limits the effectiveness of method
references as a boilerplate reducing language feature. Not sure if
anyone has anything nice and improving on this front. The point was
raised in discussion that if you care about fluency then you don't
mind duplicating every factory for readibility's sake - which might be
true.
regards,
Richard
[0] http://code.google.com/p/mutability-detector/
[1] http://code.google.com/p/mutability-detector/source/browse/trunk/MutabilityDetector/trunk/MutabilityDetector/src/main/java/org/mutabilitydetector/locations/ClassNameConverter.java?r=578
[2] http://code.google.com/p/mutability-detector/source/browse/trunk/MutabilityDetector/trunk/MutabilityDetector/src/main/java/org/mutabilitydetector/locations/ClassNameConverter.java
[3] http://code.google.com/p/mutability-detector/source/browse/trunk/MutabilityDetector/branches/jdk8lambdahack/MutabilityDetector/src/main/java/org/mutabilitydetector/locations/ClassNameConverter.java
More information about the lambda-dev
mailing list