• Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
4,641
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
98
Comments
2
Likes
10

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. FP in Java Project Lambda and beyondby Mario Fuscomario.fusco@gmail.comtwitter: @mariofusco
  • 2. Project Lambda – A Brief History• 2006 – Gosling: "We will never have closures in Java"• 2007 – 3 different proposals for closures in Java• 2008 – Reinhold: "We will never have closures in Java"• 2009 – Start of project Lambda (JSR 335) public boolean javaWillHaveClosure() { return currentYear % 2 == 1; }
  • 3. From Single Method Interfaces …public interface Comparator<T> { Functional int compare(T o1, T o2);} InterfaceCollections.sort(strings, new Comparator<String>() { public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); }}); Bulky syntax Confusion surrounding the meaning of names and this Inability to capture non-final local variables Inability to abstract over control flow
  • 4. … To Lambda ExpressionsCollections.sort(strings, (s1, s2) -> s1.compareToIgnoreCase(s2)); Lambda expression are always converted to instance of a functional interfaceComparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2); Compiler figures out the types No need of changing the JVM to create a new type for lambda expressions
  • 5. Anatomy of a lambda expression A lambda expression is like a method: it provides a list of formal parameters and a bodyThe formal parameters ofa lambda expression mayhave either inferred or s -> s.length()declared types (int x, int y) -> x + y () -> 42 Return is implicit and can be omitted (x, y, z) -> { if (x) { return y; A lambda body is either a } else { single expression or a block return z; } }
  • 6. Common JDK8 functional interfacesPredicate a property of the object passed as argumentBlock an action to be performed with the object passed as argumentFunction transform a T to a UBiFunction transform a (T, U) to a VSupplier provide an instance of a T (such as a factory)UnaryOperator a unary operator from T -> TBinaryOperator a binary operator from (T, T) -> T
  • 7. Why Lambdas?API designers can build more powerful, expressive APIsMore room for generalization o Pass behaviors to a method together with normal dataLibraries remain in control of computation o e.g. internal vs. external iterationMore opportunities for optimization o Laziness o Parallelism o Out-of-order executionMore regular and then more readable code o e.g. nested loops vs. pipelined (fluent) operationsBetter composability and reusability
  • 8. An Example: SortingComparator<Person> byAge = new Comparator<Person>() { public int compare(Person p1, Person p2) { return p1.getAge() – p2.getAge(); Functional interface }}; Lambda expressionCollections.sort(people, byAge);Comparator<Person> byAge = (p1, p2) -> p1.getAge() – p2.getAge();Collections.sort(people, (p1, p2) -> p1.getAge() – p2.getAge());
  • 9. Can We Do Better?Comparator<Person> byAge = Comparators.comparing(p -> p.getAge());Comparator<Person> byAge = Comparators.comparing(Person::getAge); Method reference Readability Collections.sort(people, comparing(Person::getAge)); Reusability Collections.sort(people, comparing(Person::getAge).reverse()); Composability Collections.sort(people, comparing(Person::getAge) .compose(comparing(Person::getName)));
  • 10. Extension methods interface Iterator<E> { boolean hasNext(); E next(); void remove(); default void forEach(Block<? super E> block) { while (hasNext()) block.accept(next()); } }Add methods to existing interfaces without breaking the backward compatibilityPrimary goal is API evolution, but useful as an inheritance mechanism on its ownAdd multiple inheritance of behavior to the always existed multiple inheritance oftype, but no multiple inheritance of state
  • 11. Extension methods interface Iterator<E> { boolean hasNext(); E next();default void remove(); { throw new UnsupportedOperationException(); } remove() default void forEach(Block<? super E> block) { while (hasNext()) block.accept(next()); } } Add methods to existing interfaces without breaking the backward compatibility Primary goal is API evolution, but useful as an inheritance mechanism on its own Add multiple inheritance of behavior to the always existed multiple inheritance of type, but no multiple inheritance of state Can be used to declare “optional” methods
  • 12. Internal VS External iteration for (Employee e : employees) { e.setSalary(e.getSalary() * 1.03); } ̶ Inherently serial ̶ Client has to manage iteration ̶ Nested loops are poorly readable employees.forEach(e -> e.setSalary(e.getSalary() * 1.03)); Not only a syntactic change!+ Library is in control opportunity for internal optimizations as parallelization, lazy evaluation, out-of-order execution+ More what, less how better readability+ Fluent (pipelined) operations better readability+ Client can pass behaviors into the API as data possibility to abstract and generalize over behavior more powerful, expressive APIs
  • 13. Streams - Efficiency with lazinessemployees.stream() .filter(e -> e.getIncome() > 50000) .map(e -> e.getName()) .forEach(System.out::println); Represents a stream of values Not a data structure: doesnt store values Source can be Collection, array, generating function, I/O .... Encourages a pipelined ( "fluent" ) usage style Operations are divided between intermediate and terminal Lazy in nature: only terminal operations actually trigger a computation
  • 14. Streams - Efficiency with laziness parallel()employees.stream() .filter(e -> e.getIncome() > 50000) .map(e -> e.getName()) .forEach(System.out::println); Represents a stream of values Not a data structure: doesnt store values Source can be Collection, array, generating function, I/O .... Encourages a pipelined ( "fluent" ) usage style Operations are divided between intermediate and terminal Lazy in nature: only terminal operations actually trigger a computation Also available a parallel stream (using the Fork/Join framework)
  • 15. So we have lambdas in Java … … now what?
  • 16. The OOP/FP dualism - OOPpublic class Bird { }public class Cat { private Bird catch; private boolean full; public void capture(Bird bird) { catch = bird; The story } public void eat() { full = true; catch = null; }}Cat cat = new Cat();Bird bird = new Bird();cat.capture(bird);cat.eat();
  • 17. The OOP/FP dualism - FPpublic class Bird { }public class Cat { public CatWithCatch capture(Bird bird) { return new CatWithCatch(bird); }}public class CatWithCatch { Immutability private final Bird catch; public CatWithCatch(Bird bird) { catch = bird; } public FullCat eat() { return new FullCat(); } Emphasis on verbs} instead of namespublic class FullCat { }BiFunction<Cat, Bird, FullCat> story = ((BiFunction<Cat, Bird, CatWithCatch>)Cat::capture) .compose(CatWithCatch::eat);FullCat fullCat = story.apply( new Cat(), new Bird() ); No need to test internal state: correctness enforced by the compiler
  • 18. Better Logging with Lambdasif (log.isDebugEnabled()) { log.debug("The answer is " + answer);} Invokes answer.toString() and does the Strings concatenation even when not necessaryCan we delay the String creation and execute it only whenstrictly necessary without (explicitly) using an if?log.debug(()-> "The answer is " + answer);public void debug(Callable<String> lambda) { if (isDebugEnabled()) { debug(lambda.call()); }}
  • 19. Side-effect isolation Reusabilityclass Player { String name; public void declareWinner(Player p) { int score; System.out.println(p.name + " wins!");} } public void winner(Player p1, Player p2) { if (p1.score > p2.score) declareWinner(p1) else declareWinner(p2); }
  • 20. Side-effect isolation Reusabilityclass Player { String name; public void declareWinner(Player p) { int score; System.out.println(p.name + " wins!");} } public void winner(Player p1, Player p2) { if (p1.score > p2.score) declareWinner(p1) else declareWinner(p2); } Separate computational logicpublic Player maxScore(Player p1, Player p2) { from side effects return p1.score > p2.score ? p1 : p2;}public void winner(Player p1, Player p2) { declareWinner(maxScore(p1, p2));}
  • 21. Side-effect isolation Reusabilityclass Player { String name; public void declareWinner(Player p) { int score; System.out.println(p.name + " wins!");} } public void winner(Player p1, Player p2) { if (p1.score > p2.score) declareWinner(p1) else declareWinner(p2); } Separate computational logicpublic Player maxScore(Player p1, Player p2) { from side effects return p1.score > p2.score ? p1 : p2;}public void winner(Player p1, Player p2) { declareWinner(maxScore(p1, p2));} declareWinner(players.stream().reduce(this::maxScore).get()) reuse maxScore as a BinaryOperator to compute the winner among a list of players
  • 22. Using Streamspublic boolean isPrimeImperative(int number) { if (number < 2) { return false; } for (int i = 2; i < (int) Math.sqrt(number) + 1; i++) { if ( number % i == 0 ) { return false; } } return true;}public boolean isPrimeFunctional(int number) { return number > 1 && Streams.intRange(2, (int) Math.sqrt(number) + 1) .noneMatch(divisor -> number % divisor == 0);}
  • 23. Working with infinite Streams take 25 (map (^2) [1..]) (take 25 (squares-of (integers))) Stream.from(1).map(_ ^ 2).take(25).toArrayStream<Integer> integers = Streams.iterate(1, i -> i + 1);int[] result = integers.map(i -> i ^ 2).limit(25).toArray();
  • 24. Null references? No, ThanksErrors source NPE is by far the most common exception in JavaBloatware source Worsen readability by making necessary to fill ourcode with null checksMeaningless Dont have any semantic meaning and in particular are thewrong way to model the absence of a value in a statically typed languageBreaks Java philosophy Java always hides pointers to developers, exceptin one case: the null pointerA hole in the type system Null has the bottom type, meaning that it canbe assigned to any reference type: this is a problem because, whenpropagated to another part of the system, you have no idea what that nullwas initially supposed to be Tony Hoare, who invented the null reference in 1965 while working on an object oriented language called ALGOL W, called its invention his “billion dollar mistake”
  • 25. Options: the functional alternativepublic abstract class Option<A> implements Iterable<A> { private Option() { } public abstract <B> Option<B> map(Function<A, B> mapper); public abstract <B> Option<B> flatMap(Function<A, Option<B>> mapper); public abstract Option<A> filter(Predicate<A> predicate); public abstract A getOrElse(A def); public abstract boolean isDefined(); public static <A> Some<A> some(A value) { if (value == null) throw new NullPointerException(); return new Some<A>(value); } public static <A> None<A> none() { return None.NONE; } public static <A> Option<A> option(A value) { return value == null ? none() : some(value); } public static final class None<A> extends Option<A> { ... } public static final class Some<A> extends Option<A> { ... }}
  • 26. Somepublic static final class Some<A> extends Option<A> { private final A value; private Some(A value) { this.value = value; } public <B> Option<B> map(Function<A, B> mapper) { return some( mapper.apply(value) ); } public <B> Option<B> flatMap(Function<A, Option<B>> mapper) { return (Option<B>) mapper.apply(value); } public Option<A> filter(Predicate<? super A> predicate) { return predicate.test(value)) ? this : None.NONE; } public A getOrElse(A def) { return value; } public boolean isDefined() { return false; }}
  • 27. Nonepublic static final class None<A> extends Option<A> { public static final None NONE = new None(); private None() { } public <B> Option<B> map(Function<A, B> mapper) { return NONE; } public <B> Option<B> flatMap(Function<A, Option<B>> mapper) { return NONE; } public Option<A> filter(Predicate<A> predicate) { return NONE; } public A getOrElse(A def) { return def; } public boolean isDefined() { return false; }}
  • 28. Example: if the value associated with a given key is a String representing a positive integer returns that integer, but returns zero in all other case@Testpublic void testReturnPositiveIntegersOrZero() { Map<String, String> param = new HashMap<String, String>(); param.put("a", "5"); param.put("b", "true"); param.put("c", "-3"); // the value of the key "a" is a String representing a // positive int so return it assertEquals(5, readPositiveIntParam(param, "a")); // returns zero since the value of the key "b" is not an int assertEquals(0, readPositiveIntParam(param, "b")); // returns zero since the value of the key "c" is a negative int assertEquals(0, readPositiveIntParam(param, "c")); // returns zero since there is no key "d" in the map assertEquals(0, readPositiveIntParam(param, "d"));}
  • 29. Null vs. Optionint readPositiveIntParam(Map<String, String> params, String name) { String value = params.get(name); if (value == null) return 0; int i = 0; try { i = Integer.parseInt(value); } catch (NumberFormatException e) { } return i < 0 ? 0 : i;}int readPositiveIntParam(Map<String, String> params, String name) { return asOption(params.get(name)) .flatMap(s -> { try { return some(Integer.parseInt(s)); } catch (NumberFormatException e) { return none(); } }) .filter(i -> i > 0) .getOrElse(0);}
  • 30. Exceptions? Yes, but …Often abused, especially for flow controlChecked Exceptions harm API extensibility/modificabilityNot composable: in presence of multiple errors only the first one isreportedIn the end just a GLORIFIED MULTILEVEL GOTO
  • 31. Exceptions? Yes, but …Often abused, especially for flow controlChecked Exceptions harm API extensibility/modificabilityNot composable: in presence of multiple errors only the first one isreportedIn the end just a GLORIFIED MULTILEVEL GOTO Either/Validation: the functional alternativeThe functional way of returning a value which can actually be one of twovalues: the error/exception (Left) or the correct value (Right) Validation<Exception, Value>
  • 32. Exceptions? Yes, but …Often abused, especially for flow controlChecked Exceptions harm API extensibility/modificabilityNot composable: in presence of multiple errors only the first one isreportedIn the end just a GLORIFIED MULTILEVEL GOTO Either/Validation: the functional alternativeThe functional way of returning a value which can actually be one of twovalues: the error/exception (Left) or the correct value (Right)Composable: can accumulate multiple errors Validation<List<Exception>, Value> Validation<Exception, Value>
  • 33. SalaryCalculatorpublic class SalaryCalculator { // B = basic + 20% public double plusAllowance(double d) { return d * 1.2; } // C = B + 10% public double plusBonus(double d) { return d * 1.1; } // D = C - 30% public double plusTax(double d) { return d * 0.7; } // E = D - 10% public double plusSurcharge(double d) { return d * 0.9; } public double calculate(double basic, boolean... bs) { double salary = basic; if (bs[0]) salary = plusAllowance(salary); if (bs[1]) salary = plusBonus(salary); if (bs[2]) salary = plusTax(salary); if (bs[3]) salary = plusSurcharge(salary); return salary; }}
  • 34. Endomorphisms & Monoids interface Endomorphism<A> extends Function<A, A> { } interface Monoid<A> { A append(A a1, A a2); A zero(); }interface EndoMonoid<A> extends Monoid<Endomorphism<A>> { @Override default Endomorphism<A> append(Endomorphism<A> a1, Endomorphism<A> a2) { return (A a) -> a2.apply(a1.apply(a)); } @Override default Endomorphism<A> zero() { return a -> a; }}
  • 35. FluentEndoMonoidpublic class FluentEndoMonoid<A> implements EndoMonoid<A> { private final Endomorphism<A> endo; public FluentEndoMonoid(Endomorphism<A> endo) { this.endo = endo; } public FluentEndoMonoid(Endomorphism<A> endo, boolean b) { this.endo = b ? endo : zero(); } public FluentEndoMonoid<A> add(Endomorphism<A> other) { return new FluentEndoMonoid<A>(append(endo, other)); } public FluentEndoMonoid<A> add(Endomorphism<A> other, boolean b) { return add(b ? other : zero()); } public Endomorphism<A> get() { return endo; } public static <A> FluentEndoMonoid<A> endo(Endomorphism<A> f, boolean b) { return new FluentEndoMonoid<A>(f, b); }}
  • 36. Functional SalaryCalculatorpublic class SalaryCalculator { public double calculate(double basic, boolean... bs) { return endo((Endomorphism<Double>) this::plusAllowance, bs[0]) .add(this::plusBonus, bs[1]) .add(this::plusTax, bs[2]) .add(this::plusSurcharge, bs[3]) .get() .apply(basic); }}
  • 37. The bottom lineJava is getting functional EMBRACE IT!
  • 38. References
  • 39. Thanks … Questions?Q AMario Fusco mario.fusco@gmail.comRed Hat – Senior Software Engineer twitter: @mariofusco