Slide 1

Slide 1 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. No part of these notes may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior, written permission of Object Computing, Inc. (OCI) Functional Groovy Gr8conf 2018 Dr Paul King @paulk_asert https://github.com/paulk-asert/functional-groovy https://speakerdeck.com/paulk/functional-groovy

Slide 2

Slide 2 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 2 WE ARE SOFTWARE ENGINEERS. We deliver mission-critical software solutions that accelerate innovation within your organization and stand up to the evolving demands of your business. • 160+ engineers • Home of Grails • Friend of Groovy • Global Footprint

Slide 3

Slide 3 text

What is functional programming? A programming paradigm that emphasizes functions

Slide 4

Slide 4 text

What is functional programming? A programming paradigm that emphasizes functions • We’ll elaborate later what “emphasizes” might mean • Groovy Closures provide first-class functional support: • Pure functions • Functions that interact with their environment • Rich capabilities: composition, partial application/currying, default argument values, static or duck typing, higher-order usage • Leverage Java-inspired functional capabilities • Lambda expressions, method references and streams (JDK8+) • Static methods in utility classes

Slide 5

Slide 5 text

What is functional programming? A programming paradigm that emphasizes functions • But Groovy doesn’t enforce functional programming • Often a matter of style • Applying certain conventions • Avoiding certain practices

Slide 6

Slide 6 text

Arguments can be named or rely on the default it name Arguments can be typed or untyped Can have no arguments Closures can be called using their call method or without Closures... © Paul King 2006-2018 def twice = { int num -> num + num } assert twice(5) == 10

Slide 7

Slide 7 text

Methods can be converted to method closures ...Closures... © Paul King 2006-2018 // ... def twiceMethod(int num) { num * 2 } assert twiceMethod(2) == 4 def alsoTwice = this.&twiceMethod assert alsoTwice(5) == 10 def g3Twice = this::twiceMethod assert g3Twice(10) == 20 // ...

Slide 8

Slide 8 text

...Closures... © Paul King 2006-2018 // ... def quadruple = twice >> twice assert quadruple(5) == 20 def forty = quadruple.curry(10) assert forty() == 40 Default arg values Forward and backward composition Partial application (curry, rcurry, ncurry) Functions as first-class citizens: variables passed as parameters return types within data structures

Slide 9

Slide 9 text

A closure is “more powerful” than a pure function Free variables are bound to the variables from their environment ...Closures... © Paul King 2006-2018 import groovy.util.logging.Log @Log class Quiz { private static int THE_ANSWER = 42 def checkAnswer = { int guess -> log.info "Guess was $guess" guess == THE_ANSWER } } def q = new Quiz() assert !q.checkAnswer(21) assert q.checkAnswer(twice(21)) def twice = { int num -> num + num } assert twice(5) == 10

Slide 10

Slide 10 text

A closure is “more powerful” than a pure function Free variables are bound to the variables from their environment ...Closures... © Paul King 2006-2018 import groovy.util.logging.Log @Log class Quiz { private static int THE_ANSWER = 42 def checkAnswer = { int guess -> log.info "Guess was $guess" guess == THE_ANSWER } } def q = new Quiz() assert !q.checkAnswer(21) assert q.checkAnswer(twice(21)) def twice = { int num -> num + num } assert twice(5) == 10 def x = 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6

Slide 11

Slide 11 text

...Closures Used for many things in Groovy: • Iterators • Callbacks • Higher-order functions • Specialized control structures • Builders • Dynamic method definition • Resource allocation • Threads • Continuation-like coding © Paul King 2006-2018

Slide 12

Slide 12 text

Applying Closures and functional style © Paul King 2006-2018 import groovy.transform.CompileStatic @CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def primes = [] def palins = [] def both = [] // calculate // primes, // palins, and // both println primes println palins println both Case study applying Closures but starting with an imperative style

Slide 13

Slide 13 text

Applying Closures and functional style © Paul King 2006-2018 import groovy.transform.CompileStatic @CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def primes = [] def palins = [] def both = [] for (int i = 0; i < nums.size(); i++) { def n = nums[i] if (isPrime(n)) primes << n if (isPalindrome(n)) palins << n if (isPrime(n) && isPalindrome(n)) both << n } println primes println palins println both Case study applying Closures but starting with an imperative style

Slide 14

Slide 14 text

Applying Closures and functional style © Paul King 2006-2018 @groovy.transform.CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def (primes, palins, both) = [[], [], []] for (int i = 0; i < nums.size(); i++) { def n = nums[i] boolean prime = isPrime(n) boolean palin = isPalindrome(n) if (prime) primes << n if (palin) palins << n if (prime && palin) both << n } println primes println palins println both Case study applying Closures; imperative style but now with an efficiency improvement

Slide 15

Slide 15 text

Applying Closures and functional style © Paul King 2006-2018 import groovy.transform.CompileStatic @CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def primes = nums.findAll(isPrime) def palins = nums.findAll(isPalindrome) def both = nums.findAll{ n -> isPalindrome(n) && isPrime(n) } println primes println palins println both Case study applying Closures but using Groovy’s map, filter, reduce operations on collections but what about efficiency and what if you only wanted to process nums once

Slide 16

Slide 16 text

Applying Closures and functional style © Paul King 2006-2018 import groovy.transform.CompileStatic @CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def primes = nums.findAll(isPrime) def palins = nums.findAll(isPalindrome) def both = primes.intersect(palins) println primes println palins println both Case study applying Closures but using Groovy’s map, filter, reduce operations on collections and an alternative efficiency improvement

Slide 17

Slide 17 text

Applying Closures and functional style © Paul King 2006-2018 import groovy.transform.CompileStatic @CompileStatic Number fac(Number n) { n == 1 ? 1G : n * fac(n - 1) } def nums = 100..200 def isPrime = { Number n -> (fac(n - 1) + 1) % n == 0 } def isPalindrome = { Number n -> n.toString().reverse() == n.toString() } def (primes, palins, both) = nums.inject([[], [], []]) { acc, n -> def pr = isPrime(n) def pa = isPalindrome(n) if (pr) acc[0] << n if (pa) acc[1] << n if (pr && pa) acc[2] << n acc } println primes println palins println both Case study applying Closures but using Groovy’s map, filter, reduce operations and processing source collection once

Slide 18

Slide 18 text

What is functional programming? Let’s revisit …

Slide 19

Slide 19 text

What is functional programming? Declarative style Control side effects Manage Mutability Functional Programming

Slide 20

Slide 20 text

What is functional programming? A programming paradigm that focuses on functions • Prefer code that embodies what rather than how • Declarative over imperative • Recursion over iteration • Control side effects (distinguish pure and impure code) • Often easier to reason about • Additional reuse possibilities • Leverage lazy evaluation, memoization and concurrency • Referential integrity • Manage immutability • Prefer values over variables • Immutable objects/collections • Functional data structures

Slide 21

Slide 21 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 36 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA

Slide 22

Slide 22 text

Are these both pure functions? Pure Functions, Closures and side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } def incrementWithSideEffect = { arg -> x++; arg + 1 }

Slide 23

Slide 23 text

Are these both pure functions? Pure Functions, Closures and side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } assert 11 == increment(10) assert x == 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6

Slide 24

Slide 24 text

Are these both pure functions? Pure Functions, Closures and side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } assert 11 == increment(10) assert x == 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6

Slide 25

Slide 25 text

Potential inputs: • Parameters • Instance state • Static/global state Potential outputs: • Return value • Instance state • Static/global state • Mutable params • Exceptions Pure Functions, Closures and side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } assert 11 == increment(10) assert x == 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6 Impure ≠ Evil • But more difficult to reason about

Slide 26

Slide 26 text

Potential inputs: • Parameters • Instance state • Static/global state Potential outputs: • Return value • Instance state • Static/global state • Mutable params • Exceptions Pure Functions, Closures and side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } assert 11 == increment(10) assert x == 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6 Context Context Impure ≠ Evil • Need to understand context

Slide 27

Slide 27 text

Potential inputs: • Parameters • Instance state • Static/global state Potential outputs: • Return value • Instance state • Static/global state • Mutable params • Exceptions Prefer pure functions; Avoid side-effects © Paul King 2006-2018 def x = 4 def increment = { arg -> arg + 1 } assert 11 == increment(10) assert x == 4 def incrementWithSideEffect = { arg -> x++; arg + 1 } assert 11 == incrementWithSideEffect(10) assert 101 == incrementWithSideEffect(100) assert x == 6 Impure ≠ Evil • Avoid when possible • Use with caution when needed

Slide 28

Slide 28 text

© Paul King 2006-2018 Referential Transparency def x, y, z, arg def doSomething = { // ... } y = 3 x = y + 1 doSomething(y) z = y + 1 // expect z == x assert x == z Will this always execute successfully?

Slide 29

Slide 29 text

© Paul King 2006-2018 Referential Transparency def x, y, z, arg def doSomething = { it = 99 // or it++ } y = 3 x = y + 1 doSomething(y) z = y + 1 // expect z == x assert x == z Will this always execute successfully?

Slide 30

Slide 30 text

© Paul King 2006-2018 Referential Transparency def x, y, z, arg def doSomething = { y = 99 // or y++ } y = 3 x = y + 1 doSomething(y) z = y + 1 // expect z == x assert x == z Will this always execute successfully?

Slide 31

Slide 31 text

© Paul King 2006-2018 Referential Transparency def x, y, z, arg def doSomething = { it.clear() } y = [1, 2, 3] x = y.size() doSomething(y) z = y.size() // expect z == x assert x == z Will this always execute successfully?

Slide 32

Slide 32 text

© Paul King 2006-2018 Referential Transparency def x, y, z, arg def doSomethingPure = { // ... } y = 3 x = y + 1 doSomethingPure(y) z = y + 1 // require z == x assert x == z Referentially transparent: expressions can be evaluated at any time, one can freely replace variables by their values and vice-versa; always return the same result given the same inputs Equal expressions can always be substituted Leads to automatic memorization, automatic concurrency, more reuse through functional abstractions

Slide 33

Slide 33 text

© Paul King 2006-2018 Referential Transparency def pythagorian(x, y) { Math.sqrt(x * x + y * y) } final int A = 4 final int B = 3 def c = pythagorian(A, B) // c = 5 assert c == 5 Referentially transparent: expressions can be evaluated at any time, one can freely replace variables by their values and vice-versa; always return the same result given the same inputs Equal expressions can always be substituted Leads to automatic memorization, automatic concurrency, more reuse through functional abstractions

Slide 34

Slide 34 text

© Paul King 2006-2018 Referential Transparency def c = 5 // replaced at compile time assert c == 5 Referentially transparent: expressions can be evaluated at any time, one can freely replace variables by their values and vice-versa; always return the same result given the same inputs Equal expressions can always be substituted Leads to automatic memorization, automatic concurrency, more reuse through functional abstractions

Slide 35

Slide 35 text

© Paul King 2006-2018 Referential Transparency assert true Referentially transparent: expressions can be evaluated at any time, one can freely replace variables by their values and vice-versa; always return the same result given the same inputs Equal expressions can always be substituted Leads to automatic memorization, automatic concurrency, more reuse through functional abstractions

Slide 36

Slide 36 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def result = [] while(s.size() > 0) { result << pow(s.pop(), s.pop()) } assert result == [1, 8, 25] assert s.size() == 0 //def pow(base, exponent) { base ** exponent } def pow(base, exponent) { exponent == 0 ? 1 : base ** exponent } First version evaluates pow parameters eagerly and according to Groovy’s laws for parameter evaluation order Hint: they are the same as for Java

Slide 37

Slide 37 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { base() ** exponent() } Lazy evaluation makes everything better, so let’s use it here All good so far, but we’ve left off the optimization

Slide 38

Slide 38 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { exponent() == 0 ? 1 : base() ** exponent() }

Slide 39

Slide 39 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { exponent() == 0 ? 1 : base() ** exponent() }

Slide 40

Slide 40 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { def e = exponent() e == 0 ? 1 : base() ** e }

Slide 41

Slide 41 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { def e = exponent() e == 0 ? 1 : base() ** e }

Slide 42

Slide 42 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def result = [] def lazyPop = { s.pop() } while(s.size() > 0) { result << pow(lazyPop, lazyPop) } assert result == [1, 8, 25] & s.size() == 0 def pow(base, exponent) { def b = base() def e = exponent() e == 0 ? 1 : b ** e }

Slide 43

Slide 43 text

© Paul King 2006-2018 Referential Transparency and Laziness def s = [2, 5, 3, 2, 0, 2] as Stack assert s.size() % 2 == 0 def get = { idx -> s[idx] } def baseIndices = s.indices.findAll{ it % 2 == 0 }.reverse() def result = baseIndices.collect{ pow(get.curry(it+1), get.curry(it)) } assert result == [1, 8, 25] def pow(base, exponent) { exponent() == 0 ? 1 : base() ** exponent() } But better to use referentially transparent version and no mutable structure in the first place. This version doesn’t leave stack empty but could be executed concurrently.

Slide 44

Slide 44 text

© Paul King 2006-2018 Applying pure functions: memoization def wotd = 'possessionlessness' println wotd.size() println wotd.toSet().size() def upper = { c -> sleep 500; c.toUpperCase() } timedWotd(wotd, upper) timedWotd(wotd, upper.memoize()) // also memoizeAtLeast(5), memoizeBetween(5, 10) timedWotd(wotd, upper.memoizeAtMost(5)) private void timedWotd(String wotd, upper) { def start = System.currentTimeMillis() def result = wotd.collect { upper(it) } def time = System.currentTimeMillis() - start assert result == ['P', 'O', 'S', 'S', 'E', 'S', 'S', 'I','O', 'N', 'L', 'E', 'S', 'S', 'N', 'E', 'S', 'S'] println time } Memorize results for pure functions Don’t try with Math.random, Coin.toss, Dice.throw, … 18 7 9008 3502 4503

Slide 45

Slide 45 text

© Paul King 2006-2018 Memoization for methods too import groovy.transform.Memoized @Memoized int sum(int n1, int n2) { println "DEBUG: $n1 + $n2 = ${n1 + n2}" n1 + n2 } 2.times { [[1, 10], [2, 20]].each { a, b -> println sum(a, b) } } DEBUG: 1 + 10 = 11 11 DEBUG: 2 + 20 = 22 22 11 22

Slide 46

Slide 46 text

When memorization goes wrong Source: http://www.commitstrip.com/en/2017/05/06/bugs-of-the-future/

Slide 47

Slide 47 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 64 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA

Slide 48

Slide 48 text

Why Immutability? Simple • Exactly one state • Potentially easier to design, implement, use, reason about & make secure Inherently referentially transparent • Potential for optimisation Can be shared freely • Including “constant” aggregations of immutables • Including persistent structures of immutables • Suitable for caching • Can even cache “pure” expressions involving immutables, e.g. 3 + 4, “string”.size(), fib(42) • Inherently thread safe

Slide 49

Slide 49 text

Immutable practices Avoid using mutator methods • But only marginal gains when using Java’s built-in collections // Avoid String invention = 'Mouse Trap' List inventions = [invention] invention = 'Better ' + invention inventions << invention assert inventions == ['Mouse Trap', 'Better Mouse Trap'] inventions.removeAll 'Mouse Trap' assert inventions == ['Better Mouse Trap'] // Prefer String firstInvention = 'Mouse Trap' List initialInventions = [firstInvention] String secondInvention = 'Better ' + firstInvention List allInventions = initialInventions + secondInvention assert allInventions == ['Mouse Trap', 'Better Mouse Trap'] List bestInventions = allInventions - firstInvention assert bestInventions == ['Better Mouse Trap']

Slide 50

Slide 50 text

Immutable practices Avoid using mutator methods Avoid Prefer list.sort() list.toSorted() list.sort(false) list.unique() list.toUnique() list.unique(false) list.reverse(true) list.reverse() list.addAll list.plus list.removeAll list.minus String or List += or << use differently named variables mutating java.util.Collections void methods, e.g. shuffle, swap, fill, copy, rotate your own non mutating variants

Slide 51

Slide 51 text

Immutable practices Avoid using mutator methods Avoid Prefer list.sort() list.toSorted() list.sort(false) list.unique() list.toUnique() list.unique(false) list.reverse(true) list.reverse() list.addAll list.plus list.removeAll list.minus String or List += or << use differently named variables mutating java.util.Collections void methods, e.g. shuffle, swap, fill, copy, rotate your own non mutating variants public class Collections { public static void shuffle(List> list) { /* ... */ } /* ... */ }

Slide 52

Slide 52 text

Immutable practices Avoid using mutator methods Avoid Prefer list.sort() list.toSorted() list.sort(false) list.unique() list.toUnique() list.unique(false) list.reverse(true) list.reverse() list.addAll list.plus list.removeAll list.minus String or List += or << use differently named variables mutating java.util.Collections void methods, e.g. shuffle, swap, fill, copy, rotate your own non mutating variants static List myShuffle(List list) { List result = new ArrayList(list) Collections.shuffle(result) result }

Slide 53

Slide 53 text

Immutability options - collections Built-in Google Collections • Numerous improved immutable collection types Groovy run-time metaprogramming import com.google.common.collect.* List animals = ImmutableList.of("cat", "dog", "horse") animals << 'fish' // => java.lang.UnsupportedOperationException def animals = ['cat', 'dog', 'horse'].asImmutable() animals << 'fish' // => java.lang.UnsupportedOperationException def animals = ['cat', 'dog', 'horse'] ArrayList.metaClass.leftShift = { throw new UnsupportedOperationException() } animals << 'fish' // => java.lang.UnsupportedOperationException

Slide 54

Slide 54 text

Immutable Classes Some Rules • Don’t provide mutators • Ensure that no methods can be overridden • Easiest to make the class final • Or use static factories & non-public constructors • Make all fields final • Make all fields private • Avoid even public immutable constants • Ensure exclusive access to any mutable components • Don’t leak internal references • Defensive copying in and out • Optionally provide equals and hashCode methods • Optionally provide toString method

Slide 55

Slide 55 text

@Immutable... Java Immutable Class • As per Joshua Bloch Effective Java © Paul King 2006-2018 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }

Slide 56

Slide 56 text

...@Immutable... Java Immutable Class • As per Joshua Bloch Effective Java © Paul King 2006-2018 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } } boilerplate

Slide 57

Slide 57 text

...@Immutable © Paul King 2006-2018 @Immutable class Person { String first, last }

Slide 58

Slide 58 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 79 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA

Slide 59

Slide 59 text

Approaches to managing collection storage Mutable © Paul King 2006-2018 ‘c’ ‘a’ ‘c’ ‘a’ Add ‘t’ Add ‘t’ Add ‘t’ ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ ‘c’ ‘a’ ‘t’ X ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ Immutable Persistent

Slide 60

Slide 60 text

Approaches to managing collection storage Mutable © Paul King 2006-2018 ‘c’ ‘a’ ‘c’ ‘a’ Add ‘t’ Add ‘t’ Add ‘t’ ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ ‘c’ ‘a’ ‘t’ X ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ Persistent Immutable

Slide 61

Slide 61 text

Approaches to managing collection storage Mutable © Paul King 2006-2018 ‘c’ ‘a’ ‘c’ ‘a’ Add ‘t’ Add ‘t’ Add ‘t’ ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ ‘c’ ‘a’ ‘t’ X ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ Persistent Immutable

Slide 62

Slide 62 text

Immutability – persistent collections Functional Java • Or Functional Groovy, clj-ds, pcollections, totallylazy @Grab('org.functionaljava:functionaljava:4.4') def pets = fj.data.List.list("cat", "dog", "horse") // buy a fish def newPets = pets.cons("fish") assert [3, 4] == [pets.length(), newPets.length()] pets newPets head tail head tail head tail head tail fish cat dog horse

Slide 63

Slide 63 text

Immutability – persistent collections Functional Java @Grab('org.functionaljava:functionaljava:4.4') def pets = fj.data.List.list("cat", "dog", "horse") def newPets = pets.cons("fish") assert [3, 4] == [pets.length(), newPets.length()] // sell the horse def remaining = newPets.removeAll{ it == 'horse' } pets newPets head tail head tail head tail head tail fish cat dog horse remaining ???

Slide 64

Slide 64 text

Immutability – persistent collections Functional Java @Grab('org.functionaljava:functionaljava:4.4') def pets = fj.data.List.list("cat", "dog", "horse") def newPets = pets.cons("fish") assert [3, 4] == [pets.length(), newPets.length()] def remaining = newPets.removeAll{ it == 'horse' } assert [3, 4, 3] == [pets, newPets, remaining]*.length() pets newPets head tail head tail head tail head tail fish cat dog horse remaining ???

Slide 65

Slide 65 text

Immutability – persistent collections Functional Java @Grab('org.functionaljava:functionaljava:4.4') def pets = fj.data.List.list("cat", "dog", "horse") def newPets = pets.cons("fish") assert [3, 4] == [pets.length(), newPets.length()] def remaining = newPets.removeAll{ it == 'horse' } assert [3, 4, 3] == [pets, newPets, remaining]*.length() pets newPets head tail head tail head tail head tail fish cat dog horse remaining head tail head tail head tail fish cat dog copy copy copy

Slide 66

Slide 66 text

Immutability – persistent collections A B C D E F G H I K ??? J original

Slide 67

Slide 67 text

Immutability – persistent collections You will see the correct results but in general, different operations may give very differing performance characteristics from what you expect • But don’t fret, smart people are working on smart structures to support a variety of scenarios. You may even have several in your current NoSQL implementation A B C D E F G H I A* C* G* K J original modified

Slide 68

Slide 68 text

Persistent data structures libraries: VAVR http://www.vavr.io/

Slide 69

Slide 69 text

Persistent data structures libraries: pcollections https://pcollections.org/

Slide 70

Slide 70 text

Persistent data structures libraries: Paguro https://github.com/GlenKPeterson/Paguro

Slide 71

Slide 71 text

Persistent data structures: Functional Java/Groovy • Singly-linked list (fj.data.List) • Lazy singly-linked list (fj.data.Stream) • Nonempty list (fj.data.NonEmptyList) • Optional value (a container of length 0 or 1) (fj.data.Option) • Immutable set using a red/black tree (fj.data.Set) • Immutable multi-way tree (a.k.a. rose tree) (fj.data.Tree) • Immutable tree-map using a red/black tree (fj.data.TreeMap) • Products (tuples) of arity 1-8 (fj.P1..P8) • Vectors of arity 2-8 (fj.data.vector.V2..V8) • Pointed lists and trees (fj.data.Zipper and fj.data.TreeZipper) • Type-safe, generic heterogeneous list (fj.data.hlist.HList) • Immutable arrays (fj.data.Array) • Disjoint union datatype (fj.data.Either) • 2-3 finger trees supporting access to the ends in amortized O(1) time (fj.data.fingertrees)

Slide 72

Slide 72 text

Functional Data Structures Source: https://forrestbrazeal.com/2016/07/27/cloudpleasers-real-world-coding-interview/

Slide 73

Slide 73 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 96 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA

Slide 74

Slide 74 text

Lazy evaluation Normal Boolean expression short-circuiting GString usage def myList = // ... if (myList != null && myList.size() > 1) { myList.remove(0) } def gs1 = "Hello at ${ new Date() }" def gs2 = "Hello at ${ -> new Date() }" sleep 5000 println gs1 println gs2

Slide 75

Slide 75 text

Lazy evaluation – Ternary (TBD) Ternary Ternary revisited def myList = // ... if (myList != null && myList.size() > 1) { myList.remove(0) } ternary(condition, Closure c, Supplier s) { … }

Slide 76

Slide 76 text

Lazy evaluation – Logging (TBD) Ternary Equiv: @Log4j Log.trace() def gs1 = "Hello at ${ new Date() }" def gs2 = "Hello at ${ -> new Date() }" sleep 5000 println gs1 println gs2

Slide 77

Slide 77 text

Lazy evaluation – JDK8 Streams (TBD) Stream is a spec of how to manipulate data IntStream.iterate( 1, i -> i + 1) // potentially infinite .map( i -> i + 2 ) // lazy .filter( i -> i > 5 ) // lazy .findFirst() // terminal operation

Slide 78

Slide 78 text

Lazy evaluation - XmlSlurper

Slide 79

Slide 79 text

Lazy evaluation - DataSet

Slide 80

Slide 80 text

Lazy evaluation @Singleton @Singleton(lazy = true) class Universe { public int ANSWER = 42 } assert 42 == Universe.instance.ANSWER

Slide 81

Slide 81 text

Lazy evaluation @Lazy // nominally expensive resource // with stats class Resource { private static alive = 0 private static used = 0 Resource() { alive++ } def use() { used++ } static stats() { "$alive alive, $used used" } } class ResourceMain { def res1 = new Resource() @Lazy res2 = new Resource() @Lazy static res3 = { new Resource() }() @Lazy(soft=true) volatile Resource res4 } new ResourceMain().with { assert Resource.stats() == '1 alive, 0 used' res2.use() res3.use() res4.use() assert Resource.stats() == '4 alive, 3 used' assert res4 instanceof Resource def expected = 'res4=java.lang.ref.SoftReference' assert it.dump().contains(expected) }

Slide 82

Slide 82 text

totallylazy library Similar to Groovy’s collection GDK methods … Except … lazy … @GrabResolver('http://repo.bodar.com/') @Grab('com.googlecode.totallylazy:totallylazy:1113') import static com.googlecode.totallylazy.Sequences.map import static com.googlecode.totallylazy.numbers.Numbers.* assert range(6, 10) == [6,7,8,9,10] assert range(6, 10, 2).forAll(even) assert range(6, 10).reduce{ a, b -> a + b } == 40 assert range(6, 10).foldLeft(0, add) == 40 assert map(range(6, 10), { it + 100 }) == [106,107,108,109,110] assert primes().take(10) == [2,3,5,7,11,13,17,19,23,29] assert range(1, 4).cycle().drop(2).take(8) == [3,4,1,2,3,4,1,2] println range(6, 1_000_000_000_000).filter(even).drop(1).take(5) // => 8,10,12,14,16 (a handful of millis later)

Slide 83

Slide 83 text

Immutability options - collections This script Produces this output (order will vary) @GrabResolver('http://repo.bodar.com/') @Grab('com.googlecode.totallylazy:totallylazy:1113') import static com.googlecode.totallylazy.Sequences.flatMapConcurrently import static com.googlecode.totallylazy.numbers.Numbers.* println flatMapConcurrently(range(6, 10)) { println it // just for logging even(it) ? [it, it+100] : [] } 9 7 8 6 10 6,106,8,108,10,110

Slide 84

Slide 84 text

GPars and TotallyLazy library © Paul King 2006-2018 @GrabResolver('http://repo.bodar.com') @Grab('com.googlecode.totallylazy:totallylazy:1113') import static groovyx.gpars.GParsExecutorsPool.withPool import static com.googlecode.totallylazy.Callables.asString import static com.googlecode.totallylazy.Sequences.sequence withPool { pool -> assert ['5', '6'] == sequence(4, 5, 6) .drop(1) .mapConcurrently(asString(), pool) .toList() } withPool { assert ['5', '6'] == [4, 5, 6] .drop(1) .collectParallel{ it.toString() } } <= Plain GPars equivalent

Slide 85

Slide 85 text

Groovy Streams https://github.com/timyates/groovy-stream @Grab('com.bloidonia:groovy-stream:0.5.2') import groovy.stream.Stream // Repeat an object indefinitely Stream s = Stream.from { 1 } assert s.take( 5 ).collect() == [ 1, 1, 1, 1, 1 ] // Use an Iterable s = Stream.from 1..3 assert s.collect() == [ 1, 2, 3 ] // Use an iterator def iter = [ 1, 2, 3 ].iterator() s = Stream.from iter assert s.collect() == [ 1, 2, 3 ] // Use a map of iterables s = Stream.from x:1..2, y:3..4 assert s.collect() == [ [x:1,y:3],[x:1,y:4],[x:2,y:3],[x:2,y:4] ]

Slide 86

Slide 86 text

Groovy Streams https://github.com/dsrkoc/monadologie import static hr.helix.monadologie.MonadComprehension.foreach def res = foreach { a = takeFrom { [1, 2, 3] } b = takeFrom { [4, 5] } yield { a + b } } assert res == [5, 6, 6, 7, 7, 8]

Slide 87

Slide 87 text

Functional Groovy https://github.com/mperry/functionalgroovy @Grab('com.github.mperry:functionalgroovy-core:0.8') import static com.github.mperry.fg.Comprehension.foreach 1.to(5).each { println it } def result = foreach { num { 1.to(2) } yield { num + 1 } } assert result.toJList() == [2, 3]

Slide 88

Slide 88 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 111 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA @Grab('one.util:streamex:0.6.6')

Slide 89

Slide 89 text

Recursion Divide and conquer strategy I don’t know how to solve this problem but I know how to break it into two smaller problems

Slide 90

Slide 90 text

Recursion Divide and conquer strategy I don’t know how to solve this problem but I know how to break it into two smaller problems E.g. I don’t know how to solve: factorial(n) but I know it is: n * factorial(n – 1) and I know factorial(1) = 1

Slide 91

Slide 91 text

Recursion © Paul King 2006-2018 def factorial = { it <= 1 ? 1G : it * factorial(it - 1) } println factorial(5) ?

Slide 92

Slide 92 text

Recursion © Paul King 2006-2018 def factorial = { it <= 1 ? 1G : it * factorial(it - 1) } println factorial(5)

Slide 93

Slide 93 text

Recursion © Paul King 2006-2018 def factorial = { it <= 1 ? 1G : it * factorial(it - 1) } def factorial factorial = { it <= 1 ? 1G : it * factorial(it - 1) } println factorial(5) def factorial = { it <= 1 ? 1G : it * call(it - 1) } println factorial(5)

Slide 94

Slide 94 text

Recursion © Paul King 2006-2018 def factorial = { it <= 1 ? 1G : it * factorial(it - 1) } def factorial factorial = { it <= 1 ? 1G : it * factorial(it - 1) } println factorial(5) def factorial = { it <= 1 ? 1G : it * call(it - 1) } println factorial(5) factorial(5) 5 * factorial(4) 5 * (4 * factorial(3)) 5 * (4 * (3 * factorial(2))) 5 * (4 * (3 * (2 * factorial(1)))) 5 * (4 * (3 * (2 * 1))) 5 * (4 * (3 * 2)) 5 * (4 * 6) 5 * 24 120

Slide 95

Slide 95 text

Recursion © Paul King 2006-2018 def factorial = { it <= 1 ? 1G : it * factorial(it - 1) } def factorial factorial = { it <= 1 ? 1G : it * factorial(it - 1) } println factorial(5) def factorial = { it <= 1 ? 1G : it * call(it - 1) } println factorial(5) Subject to StackOverflow factorial(5) 5 * factorial(4) 5 * (4 * factorial(3)) 5 * (4 * (3 * factorial(2))) 5 * (4 * (3 * (2 * factorial(1)))) 5 * (4 * (3 * (2 * 1))) 5 * (4 * (3 * 2)) 5 * (4 * 6) 5 * 24 120

Slide 96

Slide 96 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc -> n <= 1 ? acc : factorial(n - 1, n * acc) } println factorial(5, 1) factorial(5, 1) factorial(4, 5) factorial(3, 20) factorial(2, 60) factorial(1, 120) 120 def factorial = { n, acc = 1 -> n <= 1 ? acc : call(n - 1, n * acc) } println factorial(5) def factorial = { n -> def doFact = { n1, acc -> n1 <= 1 ? acc : call(n1 - 1, n1 * acc) } doFact(n, 1) } println factorial(5)

Slide 97

Slide 97 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc -> n <= 1 ? acc : factorial(n - 1, n * acc) } println factorial(5, 1) factorial(5, 1) factorial(4, 5) factorial(3, 20) factorial(2, 60) factorial(1, 120) 120 def factorial = { n, acc = 1 -> n <= 1 ? acc : call(n - 1, n * acc) } println factorial(5) def factorial = { n -> def doFact = { n1, acc -> n1 <= 1 ? acc : call(n1 - 1, n1 * acc) } doFact(n, 1) } println factorial(5)

Slide 98

Slide 98 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc -> n <= 1 ? acc : factorial(n - 1, n * acc) } println factorial(5, 1) def factorial = { n, acc = 1 -> n <= 1 ? acc : call(n - 1, n * acc) } println factorial(5) def factorial = { n -> def doFact = { n1, acc -> n1 <= 1 ? acc : call(n1 - 1, n1 * acc) } doFact(n, 1) } println factorial(5) Still subject to StackOverflow factorial(5, 1) factorial(4, 5) factorial(3, 20) factorial(2, 60) factorial(1, 120) 120

Slide 99

Slide 99 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc = 1G -> n <= 1 ? acc : factorial.trampoline(n - 1, n * acc) } println factorial(1) println factorial(5) println factorial(5).call() factorial(1) 1 factorial(5).call() tfactorial(4, 5).call() factorial(4, 5) tfactorial(3, 20).call() factorial(3, 20) tfactorial(2, 60).call() factorial(2, 60) tfactorial(1, 120).call() factorial(1, 120) 120 factorial(5) tfactorial(4, 5) groovy.lang.TrampolineClosure@50ad3bc1 trampoline() is a method available on the Closure class which returns a trampoline wrapped version of itself

Slide 100

Slide 100 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc = 1G -> n <= 1 ? acc : factorial.trampoline(n - 1, n * acc) } println factorial(1) println factorial(5) println factorial(5).call() factorial(1) 1 factorial(5).call() tfactorial(4, 5).call() factorial(4, 5) tfactorial(3, 20).call() factorial(3, 20) tfactorial(2, 60).call() factorial(2, 60) tfactorial(1, 120).call() factorial(1, 120) 120 factorial(5) tfactorial(4, 5) groovy.lang.TrampolineClosure@50ad3bc1 trampoline() is a method available on the Closure class which returns a trampoline wrapped version of itself

Slide 101

Slide 101 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc = 1G -> n <= 1 ? acc : factorial.trampoline(n - 1, n * acc) } println factorial(1) println factorial(5) println factorial(5).call() factorial(1) 1 factorial(5).call() tfactorial(4, 5).call() factorial(4, 5) tfactorial(3, 20).call() factorial(3, 20) tfactorial(2, 60).call() factorial(2, 60) tfactorial(1, 120).call() factorial(1, 120) 120 factorial(5) tfactorial(4, 5) groovy.lang.TrampolineClosure@50ad3bc1 Trampolined closure – if called, continually loops calling itself until a non-trampoline Closure result is found

Slide 102

Slide 102 text

Recursion © Paul King 2006-2018 def factorial factorial = { n, acc = 1G -> n <= 1 ? acc : factorial.trampoline(n - 1, n * acc) }.trampoline() println factorial(1) println factorial(5) tfactorial(1) factorial(1) 1 tfactorial(5) factorial(5).call() tfactorial(4, 5) ... 120 Trampolined closure – if called, continually loops calling itself until a non-trampoline Closure result is found

Slide 103

Slide 103 text

Mutual Recursion © Paul King 2006-2018 def even def odd = { n -> n == 0 ? false : even(n - 1) } even = { n -> n == 0 ? true : odd(n - 1) } assert odd(21) & even(12) def odd, even odd = { n -> n == 0 ? false : even.trampoline(n - 1) }.trampoline() even = { n -> n == 0 ? true : odd.trampoline(n - 1) }.trampoline() assert odd(4321) & even(1234) Subject to StackOverflow

Slide 104

Slide 104 text

Mutual Recursion © Paul King 2006-2018 def even def odd = { n -> n == 0 ? false : even(n - 1) } even = { n -> n == 0 ? true : odd(n - 1) } assert odd(21) & even(12) def odd, even odd = { n -> n == 0 ? false : even.trampoline(n - 1) }.trampoline() even = { n -> n == 0 ? true : odd.trampoline(n - 1) }.trampoline() assert odd(4321) & even(1234)

Slide 105

Slide 105 text

Recursion for methods © Paul King 2006-2018 import groovy.transform.TailRecursive @TailRecursive def factorial(n) { n * factorial(n - 1) } println factorial(10G) ?

Slide 106

Slide 106 text

Recursion for methods © Paul King 2006-2018 import groovy.transform.TailRecursive @TailRecursive def factorial(n) { n * factorial(n - 1) } println factorial(10G)

Slide 107

Slide 107 text

Recursion for methods © Paul King 2006-2018 import groovy.transform.TailRecursive @TailRecursive def factorial(n, acc = 1) { n <= 1 ? acc : factorial(n - 1, n * acc) } println factorial(1000G)

Slide 108

Slide 108 text

Recursion for methods © Paul King 2006-2018 public java.lang.Object factorial(java.lang.Object n, java.lang.Object acc = 1) { java.lang.Object _acc_ = acc java.lang.Object _n_ = n while (true) { _RECUR_HERE_: try { if ( _n_ <= 1) { return _acc_ } else { null: { java.lang.Object __n__ = _n_ java.lang.Object __acc__ = _acc_ _n_ = __n__ - 1 _acc_ = __n__ * __acc__ continue _RECUR_HERE_ } } } catch (org.codehaus.groovy.transform.tailrec.GotoRecurHereException ignore) { continue _RECUR_HERE_ } finally { } } }

Slide 109

Slide 109 text

Recursion Source: https://xkcd.com/1270/

Slide 110

Slide 110 text

objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. 134 1. Introduction 2. Pure Functions 3. Immutability 4. Functional Data Structures 5. Streams/Lazy evaluation 6. Recursion 7. Advanced Abstractions AGENDA

Slide 111

Slide 111 text

Word Split with Fortress © Paul King 2006-2018 Guy Steele’s StrangeLoop keynote (from slide 52 onwards for several slides): http://strangeloop2010.com/talk/presentation_file/14299/GuySteele-parallel.pdf

Slide 112

Slide 112 text

Word Split… © Paul King 2006-2018 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result } assert swords("This is a sample") == ['This', 'is', 'a', 'sample'] assert swords("Here is a sesquipedalian string of words") == ['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']

Slide 113

Slide 113 text

Word Split… © Paul King 2006-2018 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result }

Slide 114

Slide 114 text

Word Split… © Paul King 2006-2018 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result }

Slide 115

Slide 115 text

…Word Split… © Paul King 2006-2018

Slide 116

Slide 116 text

…Word Split… © Paul King 2006-2018

Slide 117

Slide 117 text

Segment(left1, m1, right1) Segment(left2, m2, right2) Segment(left1, m1 + [ ? ] + m2, right2) …Word Split… © Paul King 2006-2018

Slide 118

Slide 118 text

…Word Split… © Paul King 2006-2018 class Util { static maybeWord(s) { s ? [s] : [] } } import static Util.* @Immutable class Chunk { String s public static final ZERO = new Chunk('') def plus(Chunk other) { new Chunk(s + other.s) } def plus(Segment other) { new Segment(s + other.l, other.m, other.r) } def flatten() { maybeWord(s) } } @Immutable class Segment { String l; List m; String r public static final ZERO = new Segment('', [], '') def plus(Chunk other) { new Segment(l, m, r + other.s) } def plus(Segment other) { new Segment(l, m + maybeWord(r + other.l) + other.m, other.r) } def flatten() { maybeWord(l) + m + maybeWord(r) } }

Slide 119

Slide 119 text

…Word Split… © Paul King 2006-2018 def processChar(ch) { ch == ' ' ? new Segment('', [], '') : new Chunk(ch) } def swords(s) { s.inject(Chunk.ZERO) { result, ch -> result + processChar(ch) } } assert swords("Here is a sesquipedalian string of words").flatten() == ['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']

Slide 120

Slide 120 text

…Word Split… © Paul King 2006-2018

Slide 121

Slide 121 text

…Word Split… © Paul King 2006-2018

Slide 122

Slide 122 text

…Word Split… © Paul King 2006-2018 THREADS = 4 def pwords(s) { int n = (s.size() + THREADS - 1) / THREADS def map = new ConcurrentHashMap() (0.. Thread.start { def (min, max) = [ [s.size(), i * n].min(), [s.size(), (i + 1) * n].min() ] map[i] = swords(s[min.. map[i] }.sum().flatten() }

Slide 123

Slide 123 text

…Word Split… © Paul King 2006-2018 import static groovyx.gpars.GParsPool.withPool THRESHHOLD = 10 def partition(piece) { piece.size() <= THRESHHOLD ? piece : [piece[0.. withPool(THREADS) { partition(input).parallel.map(swords).reduce{ a, b -> a + b }.flatten() } }

Slide 124

Slide 124 text

…Guy Steele example in Groovy… © Paul King 2006-2018 def words = { s -> int n = (s.size() + THREADS - 1) / THREADS def min = (0..

Slide 125

Slide 125 text

…Guy Steele example in Groovy… © Paul King 2006-2018 GRANULARITY_THRESHHOLD = 10 THREADS = 4 println GParsPool.withPool(THREADS) { def result = runForkJoin(0, input.size(), input){ first, last, s -> def size = last - first if (size <= GRANULARITY_THRESHHOLD) { swords(s[first..> 1) forkOffChild(first, mid, s) forkOffChild(mid, last, s) childrenResults.sum() } } switch(result) { case Chunk: return maybeWord(result.s) case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) } } } Fork/Join version

Slide 126

Slide 126 text

…Guy Steele example in Groovy © Paul King 2006-2018 println GParsPool.withPool(THREADS) { def ans = input.collectParallel{ processChar(it) }.sum() switch(ans) { case Chunk: return maybeWord(ans.s) case Segment: return ans.with{ maybeWord(l) + m + maybeWord(r) } } } Just leveraging the algorithm’s parallel nature

Slide 127

Slide 127 text

Monadic style Source: https://twitter.com/typelead/status/856916971467808768

Slide 128

Slide 128 text

Monadic style intro © Paul King 2006-2018 def unit = x -> [x, ''] println sineCubeDebug(unit(3)) println sineCubeDebug << unit << 3 def round = x -> Math.round(x) println round(1.23) def roundDebug = x -> unit(round(x)) println roundDebug << 1.23 // inspired by: // http://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html Current example has been abbreviated to not really require Monadic capabilities def sine = x -> Math.sin(x) println sine(0.123) def cube = x -> x**3 println cube(0.987) sineCube = sine >> cube println sineCube(1.22) def sineDebug = x -> [Math.sin(x), 'sine was called.'] println sineDebug(0.3218) def cubeDebug = x -> [x**3, 'cube was called.'] println cubeDebug(3) def badSineCubeDebug = sineDebug << cubeDebug //badSineCubeDebug(3) // BOOM! def composeDebuggable = { f, g -> { x -> def (y, s) = g(x) def (z, t) = f(y) [z, s + t] } } println composeDebuggable(sineDebug, cubeDebug)(3) def bind = { f -> { g, h -> def (x, s) = [g, h] def (y, t) = f(x) [y, s + t] } } def sineCubeDebug = bind(sineDebug) << bind(cubeDebug) println sineCubeDebug(3, '') def lift = f -> unit << f roundDebug = lift(round) println roundDebug(0.9) def f = bind(lift(round)) << bind(sineDebug) println f(unit(27))

Slide 129

Slide 129 text

Monadic style intro © Paul King 2006-2018 def unit = x -> [x, ''] println sineCubeDebug(unit(3)) println sineCubeDebug << unit << 3 def round = x -> Math.round(x) println round(1.23) def roundDebug = x -> unit(round(x)) println roundDebug << 1.23 // inspired by: // http://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html Current example has been abbreviated to not really require Monadic capabilities def sine = x -> Math.sin(x) println sine(0.123) def cube = x -> x**3 println cube(0.987) sineCube = sine >> cube println sineCube(1.22) def sineDebug = x -> [Math.sin(x), 'sine was called.'] println sineDebug(0.3218) def cubeDebug = x -> [x**3, 'cube was called.'] println cubeDebug(3) def badSineCubeDebug = sineDebug << cubeDebug //badSineCubeDebug(3) // BOOM! def composeDebuggable = { f, g -> { x -> def (y, s) = g(x) def (z, t) = f(y) [z, s + t] } } println composeDebuggable(sineDebug, cubeDebug)(3) def bind = { f -> { g, h -> def (x, s) = [g, h] def (y, t) = f(x) [y, s + t] } } def sineCubeDebug = bind(sineDebug) << bind(cubeDebug) println sineCubeDebug(3, '') def lift = f -> unit << f roundDebug = lift(round) println roundDebug(0.9) def f = bind(lift(round)) << bind(sineDebug) println f(unit(27))

Slide 130

Slide 130 text

Monadic style intro © Paul King 2006-2018 def unit = x -> [x, ''] println sineCubeDebug(unit(3)) println sineCubeDebug << unit << 3 def round = x -> Math.round(x) println round(1.23) def roundDebug = x -> unit(round(x)) println roundDebug << 1.23 // inspired by: // http://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html Current example has been abbreviated to not really require Monadic capabilities def sine = x -> Math.sin(x) println sine(0.123) def cube = x -> x**3 println cube(0.987) sineCube = sine >> cube println sineCube(1.22) def sineDebug = x -> [Math.sin(x), 'sine was called.'] println sineDebug(0.3218) def cubeDebug = x -> [x**3, 'cube was called.'] println cubeDebug(3) def badSineCubeDebug = sineDebug << cubeDebug //badSineCubeDebug(3) // BOOM! def composeDebuggable = { f, g -> { x -> def (y, s) = g(x) def (z, t) = f(y) [z, s + t] } } println composeDebuggable(sineDebug, cubeDebug)(3) def bind = { f -> { g, h -> def (x, s) = [g, h] def (y, t) = f(x) [y, s + t] } } def sineCubeDebug = bind(sineDebug) << bind(cubeDebug) println sineCubeDebug(3, '') def lift = f -> unit << f roundDebug = lift(round) println roundDebug(0.9) def f = bind(lift(round)) << bind(sineDebug) println f(unit(27))

Slide 131

Slide 131 text

Monadic style intro © Paul King 2006-2018 def unit = x -> [x, ''] println sineCubeDebug(unit(3)) println sineCubeDebug << unit << 3 def round = x -> Math.round(x) println round(1.23) def roundDebug = x -> unit(round(x)) println roundDebug << 1.23 // inspired by: // http://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html Current example has been abbreviated to not really require Monadic capabilities def sine = x -> Math.sin(x) println sine(0.123) def cube = x -> x**3 println cube(0.987) sineCube = sine >> cube println sineCube(1.22) def sineDebug = x -> [Math.sin(x), 'sine was called.'] println sineDebug(0.3218) def cubeDebug = x -> [x**3, 'cube was called.'] println cubeDebug(3) def badSineCubeDebug = sineDebug << cubeDebug //badSineCubeDebug(3) // BOOM! def composeDebuggable = { f, g -> { x -> def (y, s) = g(x) def (z, t) = f(y) [z, s + t] } } println composeDebuggable(sineDebug, cubeDebug)(3) def bind = { f -> { g, h -> def (x, s) = [g, h] def (y, t) = f(x) [y, s + t] } } def sineCubeDebug = bind(sineDebug) << bind(cubeDebug) println sineCubeDebug(3, '') def lift = f -> unit << f roundDebug = lift(round) println roundDebug(0.9) def f = bind(lift(round)) << bind(sineDebug) println f(unit(27))

Slide 132

Slide 132 text

Monadic style intro © Paul King 2006-2018 def unit = x -> [x, ''] println sineCubeDebug(unit(3)) println sineCubeDebug << unit << 3 def round = x -> Math.round(x) println round(1.23) def roundDebug = x -> unit(round(x)) println roundDebug << 1.23 // inspired by: // http://blog.klipse.tech/javascript/2016/08/31/monads-javascript.html Current example has been abbreviated to not really require Monadic capabilities def sine = x -> Math.sin(x) println sine(0.123) def cube = x -> x**3 println cube(0.987) sineCube = sine >> cube println sineCube(1.22) def sineDebug = x -> [Math.sin(x), 'sine was called.'] println sineDebug(0.3218) def cubeDebug = x -> [x**3, 'cube was called.'] println cubeDebug(3) def badSineCubeDebug = sineDebug << cubeDebug //badSineCubeDebug(3) // BOOM! def composeDebuggable = { f, g -> { x -> def (y, s) = g(x) def (z, t) = f(y) [z, s + t] } } println composeDebuggable(sineDebug, cubeDebug)(3) def bind = { f -> { g, h -> def (x, s) = [g, h] def (y, t) = f(x) [y, s + t] } } def sineCubeDebug = bind(sineDebug) << bind(cubeDebug) println sineCubeDebug(3, '') def lift = f -> unit << f roundDebug = lift(round) println roundDebug(0.9) def f = bind(lift(round)) << bind(sineDebug) println f(unit(27))

Slide 133

Slide 133 text

158 objectcomputing.com © 2018, Object Computing, Inc. (OCI). All rights reserved. Top 10 links https://github.com/paulk-asert/functional-groovy https://speakerdeck.com/paulk/functional-groovy https://github.com/mperry/functionalgroovy http://mariogarcia.github.io/functional-groovy/ http://mariogarcia.github.io/fnz http://www.vavr.io/ https://github.com/pure4j/pure4j https://www.ibm.com/developerworks/library/j-java8idioms1/ http://www.baeldung.com/java-8-functional-interfaces https://github.com/Frege/frege