BFPG May 2014
James Livingston
"Lambdas are relegated to relative obscurity until Java makes them popular by not having them."
Java 8 support lambda expressions
No new power, just more concise syntax
Java compiler converts lambdas to any "SAM" type:
Interface or class with a Single Abstract Method
Useful for dealing with old libraries
(Integer a, Integer b) -> a + b
x -> x.toString()
i -> {int j = i + 1; return j}
s -> System.out.println(s)
Old:
CollectionUtils.filter(list, new Predicate<String>() {
public boolean evaluate(String s) {
return s.startsWith("e");
}
});
New:
CollectionUtils.filter(list, s -> s.startsWith("e"));
CollectionUtils.filter(list, String::startsWith("e"));
Lambda expressions have no inherent type
Their type is determined by their target:
Runnable r = () -> { System.out.println("Hello World!"); };
() -> { System.out.println("Hello World!"); };
You can refer to existing methods
Class::method
instance::method
Class::new
There is no support for partial application Class::staticMethod(1, _) // does not exist
_.something() // nor this
Can use them on fields or method callslist.forEach(System.out::println)
list.forEach(getOutputPrintStream()::println)
There are interfaces to represent functions
java.util.function.Function<A,B> A -> B
java.util.function.BiFunction<A,B,C> (A,B) -> C
java.util.function.Consumer<A> A -> void
java.util.function.BiConsumer<A,B> (A,B) -> void
java.util.function.Supplier<A> void -> A
java.util.function.Predicate<A> A -> boolean
java.util.function.BiPredicate<A,B> (A,B) -> boolean
java.util.function.UnaryOperator<A> A -> A
java.util.function.BinaryOperator<A> (A,A) -> A
plus ones specialised for primitives
Some useful combinators, but not all you'd want
java.util.Optional<A>
Useful replacement for null, but
Java 8 has a new Collections-ish API using lambdas
Bridge from collections using default methods
Somewhat functional API
java.util.stream.Stream<A>
java.util.stream.Collector<T,R>
Two ways to create streams
From a collection:
List<String> words = ...
Stream<String> wordStream = words.stream();
From scratchStream<A> Streams.generator(Supplier<A>)
IntStream Streams.intRange(int start, int end)
Streams have two types of operations,
terminal and non-terminal
Non-terminal operations include
map, filter, flatMap etc.
Terminal operations include
collect/reduce(fold), forEach, anyMatch, max etc.
java.util.stream.Streams class has utility methods
Stream<A> concat(Stream<A> a, Stream<A> b)
Stream<A> emptyStream()
Stream<A> iterate(A a, UnaryOperator<A> f)
Stream<C> zip(Stream<A> a, Stream<B> b, BiFunction<A,B,C> f)
and so on, with some (not all) primitive variantsMost stream operations work as you'd expect
(unless you look to closely)
flatMap() aka bind has some issues
Many core lambda devs didn't want it
Type inference problems are annoying
FlatMapper interface which lets you use mutable Consumer<R>
stream() gives you a sequential stream
You can use parallelStream() or parallel()
Exactly how the parallelism is done is
an implementation detail
'Spliterator<T>' is like an Iterator<T>
which can be asked to split for parallelism
Useful methods are not retrofitted onto Future<T>
They are on a new CompletableFuture<T>
Dealing with this is an exercise for the reader
<U> CompletionStage<U>thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
Wait, Scala's signatures are too complex?Compiling to SAMs let you use lambdas with pre-8 APIs
Many library have APIs like this
Make Java code more concise and less tedious
Although there are some problems, Streams API isn't terrible
It should prevent an explosion of incompatible APIs
Java finally has API for combining delayed computations!
It doesn't require you to manage your own thread pool
Optional has ifPresent() not forEach()
s -> s.startsWith("e")
Wouldn't it be nice to have something like Scala? _.startsWith("e")
String::isEmpty()
or if all parameters are lambda parameters
(a,b) -> a.compareTo(b) === String::compareTo
but not if you need to specify any yourself
String::startsWith("e")
Type inference often fails with nested lambdas
Scala:
val af: Future<String> = ...; val bf: Future<String> = ...
val r: Future<String> = for (a <- af; b <- bf; res <- doit(a, b)) yield res
Java 8 (making up Range):Future<String> r = af.flatMap(a -> bf.flatMap(b -> doit(a,b))
Java still doesn't have any higher-kinded types
From reading lambda-dev list, I doubt it ever will
No Monad<F> interface
Prevents some useful abstractions
Makes others incorrect
Java 8 will introduce FP ideas to Java devs
Hopefully some will be interested in learning more
We need to encourage them - bring them to BFPG!
If you're "stuck" using Java, it will be better than it was
If you're using another JVM language (e.g. Scala), then
we may see some JVM-level improvements
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
http://www.drdobbs.com/jvm/lambdas-and-streams-in-java-8-libraries/240166818