Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional programming in Java

Functional programming in Java

Deepu K Sasidharan

May 20, 2021
Tweet

More Decks by Deepu K Sasidharan

Other Decks in Programming

Transcript

  1. About me Deepu K Sasidharan JHipster co-lead developer Developer Advocate

    @ Adyen OSS aficionado, author, speaker, polyglot dev @deepu105 deepu.tech deepu105
  2. Golden rules of FP 1. No Data mutations: A data

    object should not be changed after it is created. 2. No implicit state: Hidden/Implicit state should be avoided. In functional programming state is not eliminated, instead, it’s made visible and explicit
  3. Roughly translates to 1. No side effects: An operation should

    not change any state outside of its functional scope. I.e, A function should only return a value to the invoker and should not affect any external state. 2. Pure functions only: Functional code is idempotent. A function should return values only based on the arguments passed and should not affect(side-effect) or depend on global state. Such functions always produce the same result for the same arguments.
  4. What makes a language suitable for FP • Strong type

    system • Functional data structures • Pure functions • Higher-order-functions • Closures • Currying • Recursion • Lazy evaluations • Referential transparency
  5. Java can be a Functional style language We are talking

    about the latest version of course!
  6. FP support in JAVA • Strong type system 😍 •

    Functional data structures • Pure functions • Higher-order-functions • Closures • Currying • Recursion • Lazy evaluations • Referential transparency
  7. Type system Java has a strong type system with generics

    and local type inference with var keyword and with in lambda arguments var myList = new ArrayList<String>(); var out = mapForEach(list, it -> it.length());
  8. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions • Higher-order-functions • Closures • Currying • Recursion • Lazy evaluations • Referential transparency
  9. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions • Closures • Currying • Recursion • Lazy evaluations • Referential transparency
  10. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😐 • Closures • Currying • Recursion • Lazy evaluations • Referential transparency
  11. Higher-order-function with lambda Objects are first class citizens in Java

    and we can pass objects initialized inline as well public class LambdaTest { public static void main(String[] args) { var thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Hello from thread"); } }); thread.run(); System.out.println("Hello from main"); } }
  12. Higher-order-function with lambda Objects are first class citizens in Java

    and we can pass objects initialized inline as well public class LambdaTest { public static void main(String[] args) { var thread = new Thread(new Runnable() { @Override public void run() -> { System.out.println("Hello from thread"); } }); thread.run(); System.out.println("Hello from main"); } }
  13. Higher-order-function with lambda Objects are first class citizens in Java

    and we can pass objects initialized inline as well public class LambdaTest { public static void main(String[] args) { var thread = new Thread(() -> { System.out.println("Hello from thread"); }); thread.run(); System.out.println("Hello from main"); } }
  14. Higher-order-function with lambda How can we do this for our

    own stuff? @FunctionalInterface // this doesn't do anything it is just informative. public interface FnFactory<T, S> { // The interface defines the contract for the anonymous class S execute(T it); }
  15. Higher-order-function with lambda How can we do this for our

    own stuff? // The method takes an array and an instance of FnFactory as arguments static <T, S> ArrayList<S> mapForEach(List<T> arr, FnFactory<T, S> fn) { var newArray = new ArrayList<S>(); // We are executing the method from the FnFactory instance arr.forEach(t -> newArray.add( fn.execute(t))); return newArray; }
  16. Higher-order-function with lambda How can we do this for our

    own stuff? var out = mapForEach(list, new FnFactory<String, Object>() { @Override public Object execute(final String it) { return it.length(); } }); System.out.println(out); OR var out = mapForEach(list, it -> it.length()); System.out.println(out);
  17. Higher-order-function with lambda Built in functional interfaces under java.util.function •

    UnaryOperator - single arg and return of same types • BinaryOperator - two args and return of same types • Function - single arg and return of different types • Consumer - single arg and no return • Predicate - single arg and boolean return • Supplier - no args just a return
  18. Higher-order-function with lambda Simplify to use built in interface static

    <T, S> ArrayList<S> mapForEach(List<T> arr, Function<T, S> fn) { var newArray = new ArrayList<S>(); // We are executing the method from the Function instance arr.forEach(t -> newArray.add(fn.apply(t))); return newArray; } var out = mapForEach(list, it -> it.length()); System.out.println(out);
  19. Higher-order-function with lambda Built in higher order functions, chainable pipelines

    and monad like stuff • java.util.Collections ◦ Sort, binarySearch, min, max • Java.util.stream (mostly pure functions) ◦ map, reduce, filter, find, forEach, flatMap and so on • Optional ◦ ifPresent, orElse, filter, map and so on • Anything that accepts anonymous classes
  20. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😌 • Closures 😐 • Currying • Recursion • Lazy evaluations • Referential transparency
  21. Closures with lambda Lambda closures can memorize its outer context

    // this is a higher-order-function that returns // an instance of Function interface Function<Integer, Integer> add(final int x) { // this is a closure, i.e, a variable holding // an anonymous inner class instance of the Function interface // which uses variables from the outer scope return new Function<Integer, Integer>() { @Override public Integer apply(Integer y) { // variable x is obtained from the outer // scope of this method which is declared as final return x + y; } }; } OR Function<Integer, Integer> add(final int x) { return y -> x + y; }
  22. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😌 • Closures 😌 • Currying 😐 • Recursion • Lazy evaluations • Referential transparency
  23. Closures with lambda Lambda closures can be used to create

    curried/partial functions ClosureSample sample = new ClosureSample(); // we are currying the add method to create more variations Function<Integer, Integer> add10 = sample.add(10); Function<Integer, Integer> add20 = sample.add(20); Function<Integer, Integer> add30 = sample.add(30); System.out.println(add10.apply(5)); // 15 System.out.println(add20.apply(5)); // 25 System.out.println(add30.apply(5)); // 35
  24. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😌 • Closures 😌 • Currying 😌 • Recursion 😐 • Lazy evaluations • Referential transparency
  25. Recursion Factorial with imperative Iterative approach public class FactorialSample {

    // benchmark 9.645 ns/op static long factorial(long num) { long result = 1; for (; num > 0; num--) { result *= num; } return result; } public static void main(String[] args) { System.out.println(factorial(20)); // 2432902008176640000 } }
  26. Recursion Factorial with recursive approach public class FactorialSample { //

    benchmark 19.567 ns/op static long factorialRec(long num) { return num == 1 ? 1 : num * factorialRec(num - 1); } public static void main(String[] args) { System.out.println(factorialRec(20)); // 2432902008176640000 } }
  27. Recursion Factorial with tail recursive approach public class FactorialSample {

    // benchmark 16.701 ns/op static long factorialTailRec(long num) { return factorial(1, num); } static long factorial(long accumulator, long val) { return val == 1 ? accumulator : factorial(accumulator * val, val - 1); } public static void main(String[] args) { System.out.println(factorialTailRec(20)); // 2432902008176640000 } }
  28. Recursion Factorial with streams public class FactorialSample { // benchmark

    59.565 ns/op static long factorialStream(long num) { return LongStream.rangeClosed(1, num) .reduce(1, (n1, n2) -> n1 * n2); } public static void main(String[] args) { System.out.println(factorialStream(20)); // 2432902008176640000 } }
  29. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😌 • Closures 😌 • Currying 😌 • Recursion 😐 • Lazy evaluations 😐 • Referential transparency
  30. Lazy evaluations public static void main(String[] args) { // This

    is a lambda expression behaving as a closure UnaryOperator<Integer> add = t -> { System.out.println("executing add"); return t + t; }; // This is a lambda expression behaving as a closure UnaryOperator<Integer> multiply = t -> { System.out.println("executing multiply"); return t * t; }; // Lambda closures are passed instead of plain functions System.out.println(addOrMultiply(true, add, multiply, 4)); System.out.println(addOrMultiply(false, add, multiply, 4)); } // This is a higher-order-function static <T, R> R addOrMultiply( boolean add, Function<T, R> onAdd, Function<T, R> onMultiply, T t ) { // Java evaluates expressions on ?: // lazily hence only the required method is executed return (add ? onAdd.apply(t) : onMultiply.apply(t)); }
  31. FP support in JAVA • Strong type system 😍 •

    Functional data structures 😍 • Pure functions 😌 • Higher-order-functions 😌 • Closures 😌 • Currying 😌 • Recursion 😐 • Lazy evaluations 😐 • Referential transparency 😓