functional groovy

99
© ASERT 2006-2013 Dr Paul King @paulk_asert http:/slideshare.net/paulk_asert/functional-groovy https://github.com/paulk-asert/functional-groovy Functional Groov

Upload: paul-king

Post on 12-May-2015

2.508 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: functional groovy

© A

SE

RT

2006

-2013

Dr Paul King

@paulk_asert

http:/slideshare.net/paulk_asert/functional-groovy

https://github.com/paulk-asert/functional-groovy

Functional Groov

Page 2: functional groovy

Topics

Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

• Gpars & Concurrency

• Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 3: functional groovy

Introduction

• What is functional programming? – Favour evaluation of composable

expressions over execution of commands

– Encourage particular idioms such as side-

effect free functions & immutability

• And why should I care?

– Declarative understandable code

– Reduction of errors

– Better patterns and approaches to design

– Improved reusability

– Leverage concurrency

© A

SE

RT

2006-2

013

Page 4: functional groovy

What makes up functional style?

• Functions, Closures, Lambdas, Blocks as

first-class citizens

• Higher order functions

• Mutable vs Immutable data structures

• Recursion

• Lazy vs Eager evaluation

• Declarative vs Imperative style

• Advanced Techniques – Memoization, Trampolines, Composition and Curry

• Compile-time safety

• Concurrency

Page 5: functional groovy

Closures...

© A

SE

RT

2006-2

013

def twice = { int num -> num + num } assert twice(5) == 10 assert twice.call(6) == 12 def twice10 = { 2 * 10 } assert 20 == twice10() def triple = { arg -> arg * 3 } assert triple(5) == 15 def alsoTriple = { it * 3 } assert alsoTriple(6) == 18 def quadruple = { arg = 2 -> twice(arg) * 2 } assert quadruple(5) == 20 assert quadruple() == 8 // ...

Page 6: functional groovy

...Closures...

© A

SE

RT

2006-2

013

// ... def callWith5(Closure c) { c(5) } assert 15 == callWith5(triple) def twiceMethod(int num) { num * 2 } assert twiceMethod(2) == 4 def alsoTwice = this.&twiceMethod assert alsoTwice(5) == 10 def alsoQuadruple = twice >> twice assert alsoQuadruple(5) == 20 def forty = quadruple.curry(10) assert forty() == 40 assert [10, 15, 20] == [twice, triple, quadruple].collect{ it(5) } assert 45 == [alsoTwice, alsoTriple, alsoQuadruple].sum{ it(5) }

Page 7: functional groovy

...Closures

• Used for many things in Groovy: • Iterators

• Callbacks

• Higher-order functions

• Specialized control structures

• Dynamic method definition

• Resource allocation

• Threads

• Continuation-like coding

© A

SE

RT

2006-2

013

def houston(Closure doit) { (10..1).each { count -> doit(count) } } houston { println it }

new File('/x.txt').eachLine { println it }

3.times { println 'Hi' } [0, 1, 2].each { number -> println number } [0, 1, 2].each { println it} def printit = { println it } [0, 1, 2].each printit

Page 8: functional groovy

© A

SE

RT

2006-2

013

Better Design Patterns: Builder

<html> <head> <title>Hello</title> </head> <body> <ul> <li>world 1</li> <li>world 2</li> <li>world 3</li> <li>world 4</li> <li>world 5</li> </ul> </body> </html>

import groovy.xml.* def page = new MarkupBuilder() page.html { head { title 'Hello' } body { ul { for (count in 1..5) { li "world $count" } } } }

• Markup Builder

Page 9: functional groovy

...Better File Manipulation

© A

SE

RT

2006-2

013

def out = new File('result.txt') out.delete() new File('..').eachFileRecurse { file -> if (file.name.endsWith('.groovy')) { file.eachLine { line, num -> if (line.toLowerCase().contains('groovy')) out << "File '$file' on line $num\n$line\n" } } }

Page 10: functional groovy

...DSL example...

© A

SE

RT

2006-2

013

show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show the square_root of 100 // ==> 10.0

Inspiration for this example came from …

Page 11: functional groovy

...DSL example

© A

SE

RT

2006-2

013

// Japanese DSL using GEP3 rules Object.metaClass.を = Object.metaClass.の = { clos -> clos(delegate) } まず = { it } 表示する = { println it } 平方根 = { Math.sqrt(it) } まず 100 の 平方根 を 表示する // First, show the square root of 100 // => 10.0

// source: http://d.hatena.ne.jp/uehaj/20100919/1284906117

// http://groovyconsole.appspot.com/edit/241001

Page 12: functional groovy

interface Calc { def execute(n, m) } class CalcByMult implements Calc { def execute(n, m) { n * m } } class CalcByManyAdds implements Calc { def execute(n, m) { def result = 0 n.times { result += m } return result } } def sampleData = [ [3, 4, 12], [5, -5, -25] ] Calc[] multiplicationStrategies = [ new CalcByMult(), new CalcByManyAdds() ] sampleData.each {data -> multiplicationStrategies.each {calc -> assert data[2] == calc.execute(data[0], data[1]) } }

def multiplicationStrategies = [ { n, m -> n * m }, { n, m -> def total = 0; n.times{ total += m }; total }, { n, m -> ([m] * n).sum() } ] def sampleData = [ [3, 4, 12], [5, -5, -25] ] sampleData.each{ data -> multiplicationStrategies.each{ calc -> assert data[2] == calc(data[0], data[1]) } }

Language features instead of Patterns

(c) A

SE

RT

2006

-2013

Strategy Pattern

with interfaces

with closures

Page 13: functional groovy

Topics

• Intro to Functional Style

Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

• GPars & Concurrency

• Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 14: functional groovy

Pure Functions, Closures, Side-effects

© A

SE

RT

2006-2

013

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

Page 15: functional groovy

Referential Transparency

© A

SE

RT

2006-2

013

def x, y, z, arg def method = { // ... } y = 3 arg = y x = y + 1 method(arg) z = y + 1 // z = x assert x == z 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

Page 16: functional groovy

Show me the code

PrimesPalindromes.groovy, Composition.groovy, Memoize.groovy, FactorialTrampoline.groovy

Page 17: functional groovy

Tail Recursion

• https://github.com/jlink/tailrec

© A

SE

RT

2006-2

013

@TailRecursive def factorial(n, acc = 1) { n <= 1 ? acc : factorial(n - 1, n * acc) } println factorial(1000G)

Page 18: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

Immutability & Persistent Data Structures

• Laziness & Strictness

• GPars & Concurrency

• Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 19: functional groovy

Immutability • An object’s value doesn’t change once created

• Examples – Groovy has primitives & their wrapper classes,

Strings, null

– “constants” (final reference fields)

• With some caveats about what they point to

– Basic enum values

• With some caveats on complex enums

– Numerous (effectively) immutable classes • java.awt.Color, java.net.URI, java.util.UUID, java.lang.Class,

java.util.Date, java.math.BigInteger, java.math.BigDecimal – Your own carefully written classes

– Very careful use of aggregation/collection classes

– Special immutable aggregation/collection classes

Page 20: functional groovy

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

Page 21: functional groovy

Approaches to managing collection storage

• Mutable • Persistent

© A

SE

RT

2006-2

013

• Immutable

‘c’ ‘a’ ‘c’ ‘a’

Add ‘t’ Add ‘t’ Add ‘t’

‘c’ ‘a’ ‘t’ ‘c’ ‘a’

‘c’ ‘a’ ‘t’

X

‘c’

‘a’

‘t’

‘c’

‘a’

Page 22: functional groovy

Approaches to managing collection storage

• Mutable • Persistent

© A

SE

RT

2006-2

013

• Immutable

‘c’ ‘a’ ‘c’ ‘a’

Add ‘t’ Add ‘t’ Add ‘t’

‘c’ ‘a’ ‘t’ ‘c’ ‘a’

‘c’ ‘a’ ‘t’

X

‘c’

‘a’

‘t’

‘c’

‘a’

Page 23: functional groovy

Immutable practices

• Using mutating style

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']

Page 24: functional groovy

Immutable practices

• Using mutating style

– We could possibly get away with this code here but it

has some debatable code smells

• (1) add a reference to a mutable list

• (2) change string reference losing original

• (3),(4) mutate list

• (4) duplicate first invention because original lost

String invention = 'Mouse Trap' List inventions = [invention] //(1) invention = 'Better ' + invention //(2) inventions << invention //(3) assert inventions == ['Mouse Trap', 'Better Mouse Trap'] inventions.removeAll 'Mouse Trap' //(4) assert inventions == ['Better Mouse Trap']

Page 25: functional groovy

Approaches to managing collection storage

• Mutable • Persistent

© A

SE

RT

2006-2

013

• Immutable

‘c’ ‘a’ ‘c’ ‘a’

Add ‘t’ Add ‘t’ Add ‘t’

‘c’ ‘a’ ‘t’ ‘c’ ‘a’

‘c’ ‘a’ ‘t’

X

‘c’

‘a’

‘t’

‘c’

‘a’

Page 26: functional groovy

Immutable practices

• Avoid using mutator methods

Avoid Prefer

list.sort() list.sort(false)

list.unique() 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

Page 27: functional groovy

Immutable practices

• Avoid using mutator methods

Avoid Prefer

list.sort() list.sort(false)

list.unique() 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) { /* ... */ } /* ... */ }

Page 28: functional groovy

Immutable practices

• Avoid using mutator methods

Avoid Prefer

list.sort() list.sort(false)

list.unique() 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 }

Page 29: functional groovy

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']

Page 30: functional groovy

Immutability options - collections

• Built-in

• Google Collections

– Numerous improved immutable collection types

• Groovy run-time metaprogramming

import com.google.common.collect.* List<String> 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

Page 31: functional groovy

Approaches to managing collection storage

• Mutable • Persistent

© A

SE

RT

2006-2

013

• Immutable

‘c’ ‘a’ ‘c’ ‘a’

Add ‘t’ Add ‘t’ Add ‘t’

‘c’ ‘a’ ‘t’ ‘c’ ‘a’

‘c’ ‘a’ ‘t’

X

‘c’

‘a’

‘t’

‘c’

‘a’

Page 32: functional groovy

Immutability – persistent collections

• Functional Java – Or Functional Groovy, clj-ds, pcollections, totallylazy

@Grab('org.functionaljava:functionaljava:3.1') 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

Page 33: functional groovy

Immutability – persistent collections

• Functional Java

@Grab('org.functionaljava:functionaljava:3.1') 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

???

Page 34: functional groovy

Immutability – persistent collections

• Functional Java

@Grab('org.functionaljava:functionaljava:3.1') 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

???

Page 35: functional groovy

Immutability – persistent collections

• Functional Java

@Grab('org.functionaljava:functionaljava:3.1') 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

Page 36: functional groovy

Immutability – persistent collections

A

B C

D E F G

H I K

???

J

original

Page 37: functional groovy

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

Page 38: functional groovy

Reality check

• OK, do I have to write this myself? – Might pay to try some simple ones. Take a look at Eric

Lippert’s blog on some C# implementations. Here is

the first part (Part 1: Kinds of Immutability):

http://blogs.msdn.com/ericlippert/archive/2007/11/13/

immutability-in-c-part-one-kinds-of-immutability.aspx

– Also consider

• Part 2: Simple Immutable Stack, Part 3: Covariant Immutable

Stack, Part 4: Immutable Queue, Part 6: Simple Binary Tree

– There are probably plenty of implementations you can

already use

– See also: Purely Functional Data Structures by Chris

Okasak, Cambridge University Press (1999)

• It turns out you use trees for nearly everything!

Page 39: functional groovy

Reality check

• Functional Java persistent data structures – 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)

Page 40: functional groovy

Reality check

• OK, have we achieved something simpler? – It depends. Understanding the insides of persistent

data structures can be very hard

• But as you move towards more complex systems and more

concurrent systems, not having to worry about which

threads are mutating what and when usually outweighs the

complexities of using persistent data structures

• Arguing for impure in Haskell:

http://www.cse.unsw.edu.au/~benl/papers/thesis/lippmeier-

impure-world.pdf

– They still don’t solve all of the problems. For any

significant problem you will have multiple threads

working on the solution. In some sense we have just

moved the problem but at least we have separated

concerns.

• You might combine with message passing (actors) or

dataflow or software transactional memory (STM)

Page 41: functional groovy

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

Page 42: functional groovy

@Immutable...

• Java Immutable Class – As per Joshua Bloch

Effective Java

© A

SE

RT

2006-2

013

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 + ")"; } }

Page 43: functional groovy

...@Immutable...

• Java Immutable Class – As per Joshua Bloch

Effective Java

© A

SE

RT

2006-2

013

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

Page 44: functional groovy

...@Immutable

© A

SE

RT

2006-2

013

@Immutable class Person { String first, last }

Page 45: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

Laziness & Strictness

• GPars & Concurrency

• Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 46: functional groovy

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)

Page 47: functional groovy

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

Page 48: functional groovy

GPars and TotallyLazy library

© A

SE

RT

2006-2

013

@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

Page 49: functional groovy

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] ]

Page 50: functional groovy

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]

Page 51: functional groovy

Functional Groovy

• https://github.com/mperry/functionalgroovy

@GrabResolver('https://oss.sonatype.org/content/groups/public') @Grab('com.github.mperry:functionalgroovy-core:0.2-SNAPSHOT') @Grab('org.functionaljava:functionaljava:3.1') 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]

Page 52: functional groovy

Show me the code

LazyMain.groovy

Page 53: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

GPars & Concurrency

• Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 54: functional groovy

Ralph Johnson: Parallel Programming

• Styles of parallel programming – Threads and locks

• Nondeterministic, low-level, rumored humans can do this

– Asynchronous messages e.g. Actors –

no or limited shared memory • Nondeterministic, ok for I/O but be careful with side-effects

– Sharing with deterministic restrictions

e.g. Fork-join • Hopefully deterministic semantics, not designed for I/O

– Data parallelism • Deterministic semantics, easy, efficient, not designed for I/O

© A

SE

RT

2006-2

013

http://strangeloop2010.com/talk/presentation_file/14485/Johnson-DataParallelism.pdf

Each approach has some caveats

Page 55: functional groovy

GPars • http://gpars.codehaus.org/

• Library classes and DSL sugar providing

intuitive ways for Groovy developers to

handle tasks concurrently. Logical parts:

– Data Parallelism features use JSR-166y Parallel Arrays

to enable multi-threaded collection processing

– Asynchronous functions extend the Java 1.5 built-in

support for executor services to enable multi-threaded

closure processing

– Dataflow Concurrency supports natural shared-memory

concurrency model, using single-assignment variables

– Actors provide an implementation of Erlang/Scala-like

actors including "remote" actors on other machines

– Safe Agents provide a non-blocking mt-safe reference to

mutable state; inspired by "agents" in Clojure

© A

SE

RT

2006-2

013

Page 56: functional groovy

Coordination approaches S

ourc

e: R

eG

inA

– G

roovy in

Actio

n, 2

nd e

ditio

n

Data Parallelism:

Fork/Join

Map/Reduce

Fixed coordination

(for collections)

Actors Explicit coordination

Safe Agents Delegated coordination

Dataflow Implicit coordination

Page 57: functional groovy

GPars: Choosing approaches F

or

mo

re d

eta

ils s

ee: h

ttp://g

pars

.co

de

ha

us.o

rg/C

once

pts

+co

mp

are

d

Parallel

Collections

Data Parallelism

Task

Parallelism

Streamed Data

Parallelism

Fork/

Join

Dataflow

operators

CSP

Actors

Dataflow tasks

Actors

Asynch fun’s

CSP

Fork/

Join

Immutable

Stm, Agents

Special collections

Synchronization

Linear Recursive

Linear

Recursive

Shared

Data

Irregular Regular

Page 58: functional groovy

Groovy Sequential Collection

© A

SE

RT

2006-2

013

def oneStarters = (1..30) .collect { it ** 2 } .findAll { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.max() == 196 assert oneStarters.sum() == 747

Page 59: functional groovy

GPars Parallel Collections…

© A

SE

RT

2006-2

013

import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30) .collectParallel { it ** 2 } .findAllParallel { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.maxParallel() == 196 assert oneStarters.sumParallel() == 747 }

Page 60: functional groovy

…GPars Parallel Collections

• Suitable when – Each iteration is independent, i.e. not:

fact[index] = index * fact[index - 1]

– Iteration logic doesn’t use non-thread safe code

– Size and indexing of iteration are important

© A

SE

RT

2006-2

013

import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30) .collectParallel { it ** 2 } .findAllParallel { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.maxParallel() == 196 assert oneStarters.sumParallel() == 747 }

Page 61: functional groovy

Parallel Collection Variations

• Apply some Groovy metaprogramming

© A

SE

RT

2006-2

013

import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30).makeConcurrent() .collect { it ** 2 } .findAll { it ==~ '1.*' } .findAll { it ==~ '...' } assert oneStarters == [100, 121, 144, 169, 196] }

import groovyx.gpars.ParallelEnhancer def nums = 1..5 ParallelEnhancer.enhanceInstance(nums) assert [1, 4, 9, 16, 25] == nums.collectParallel{ it * it }

Page 62: functional groovy

GPars parallel methods for collections Transparent Transitive? Parallel Lazy?

any { ... } anyParallel { ... } yes

collect { ... } yes collectParallel { ... }

count(filter) countParallel(filter)

each { ... } eachParallel { ... }

eachWithIndex { ... } eachWithIndexParallel { ... }

every { ... } everyParallel { ... } yes

find { ... } findParallel { ... }

findAll { ... } yes findAllParallel { ... }

findAny { ... } findAnyParallel { ... }

fold { ... } foldParallel { ... }

fold(seed) { ... } foldParallel(seed) { ... }

grep(filter) yes grepParallel(filter)

groupBy { ... } groupByParallel { ... }

max { ... } maxParallel { ... }

max() maxParallel()

min { ... } minParallel { ... }

min() minParallel()

split { ... } yes splitParallel { ... }

sum sumParallel // foldParallel +

Transitive means result is automatically transparent; Lazy means fails fast

For

mo

re d

eta

ils s

ee R

eG

inA

or

the G

Pa

rs d

ocu

me

nta

tion

Page 63: functional groovy

GPars: Map-Reduce

© A

SE

RT

2006-2

013

import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30).parallel .map { it ** 2 } .filter { it ==~ '1.*' } assert oneStarters.collection == [1, 16, 100, 121, 144, 169, 196] // aggregations/reductions assert oneStarters.max() == 196 assert oneStarters.reduce { a, b -> a + b } == 747 assert oneStarters.sum() == 747 }

Page 64: functional groovy

GPars parallel array methods

Method Return Type

combine(initValue) { ... } Map

filter { ... } Parallel array

collection Collection

groupBy { ... } Map

map { ... } Parallel array

max() T

max { ... } T

min() T

min { ... } T

reduce { ... } T

reduce(seed) { ... } T

size() int

sort { ... } Parallel array

sum() T

parallel // on a Collection Parallel array

For

mo

re d

eta

ils s

ee R

eG

inA

or

the G

Pa

rs d

ocu

me

nta

tion

Page 65: functional groovy

Parallel Collections vs Map-Reduce

Fork Fork

Join Join

Map

Map

Reduce

Map

Map

Reduce

Reduce

Map

Filter

Filter Map

Page 66: functional groovy

Concurrency challenge…

• Suppose we have the following

calculation involving several functions:

• And we want to use our available cores …

© A

SE

RT

2006-2

013

// example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10

Page 67: functional groovy

…Concurrency challenge…

• We can analyse the example’s task graph:

© A

SE

RT

2006-2

013

// example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10

f2

f3

f1

f4

a a

b

c

d

f

Page 68: functional groovy

…Concurrency challenge…

• Manually using asynchronous functions:

© A

SE

RT

2006-2

013

// example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import static groovyx.gpars.GParsPool.withPool withPool(2) { def a = 5 def futureB = f1.callAsync(a) def c = f2(a) def d = f3(c) def f = f4(futureB.get(), d) assert f == 10 }

f2

f3

f1

f4

a a

b

c

d

f

Page 69: functional groovy

…Concurrency challenge

• And with GPars Dataflows:

© A

SE

RT

2006-2

013

def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { a = 5 } task { b = f1(a) } task { c = f2(a) } task { d = f3(c) } task { f = f4(b, d) } assert f == 10 }

f2

f3

f1

f4

a a

b

c

d

f

Page 70: functional groovy

…Concurrency challenge

• And with GPars Dataflows:

© A

SE

RT

2006-2

013

def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { f = f4(b, d) } task { d = f3(c) } task { c = f2(a) } task { b = f1(a) } task { a = 5 } assert f == 10 }

f2

f3

f1

f4

a a

b

c

d

f

Page 71: functional groovy

GPars: Dataflows...

© A

SE

RT

2006-2

013

import groovyx.gpars.dataflow.DataFlows import static groovyx.gpars.dataflow.DataFlow.task final flow = new DataFlows() task { flow.result = flow.x + flow.y } task { flow.x = 10 } task { flow.y = 5 } assert 15 == flow.result

new DataFlows().with { task { result = x * y } task { x = 10 } task { y = 5 } assert 50 == result }

5 10

y x

*

Page 72: functional groovy

...GPars: Dataflows...

• Evaluating:

© A

SE

RT

2006-2

013

import groovyx.gpars.dataflow.DataFlows import static groovyx.gpars.dataflow.DataFlow.task final flow = new DataFlows() task { flow.a = 10 } task { flow.b = 5 } task { flow.x = flow.a - flow.b } task { flow.y = flow.a + flow.b } task { flow.result = flow.x * flow.y } assert flow.result == 75

b

10 5

a

+ -

*

result = (a – b) * (a + b)

x y

Question: what happens if I change the order of the task statements here?

Page 73: functional groovy

...GPars: Dataflows...

• Naive attempt for loops

© A

SE

RT

2006-2

013

import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task final flow = new Dataflows() [10, 20].each { thisA -> [4, 5].each { thisB -> task { flow.a = thisA } task { flow.b = thisB } task { flow.x = flow.a - flow.b } task { flow.y = flow.a + flow.b } task { flow.result = flow.x * flow.y } println flow.result } } // => java.lang.IllegalStateException: A DataflowVariable can only be assigned once.

... task { flow.a = 10 } ... task { flow.a = 20 }

Don’t do this!

X

Page 74: functional groovy

...GPars: Dataflows...

© A

SE

RT

2006-2

013

import groovyx.gpars.dataflow.DataflowStream import static groovyx.gpars.dataflow.Dataflow.* final streamA = new DataflowStream() final streamB = new DataflowStream() final streamX = new DataflowStream() final streamY = new DataflowStream() final results = new DataflowStream() operator(inputs: [streamA, streamB], outputs: [streamX, streamY]) { a, b -> streamX << a - b; streamY << a + b } operator(inputs: [streamX, streamY], outputs: [results]) { x, y -> results << x * y } [[10, 20], [4, 5]].combinations().each{ thisA, thisB -> task { streamA << thisA } task { streamB << thisB } } 4.times { println results.val }

b

10

10

20

20

4

5

4

5

a

+ -

*

84

75

384

375

Page 75: functional groovy

...GPars: Dataflows

• Suitable when: – Your algorithms can be expressed as mutually-

independent logical tasks

• Properties: – Inherently safe and robust (no race conditions or

livelocks)

– Amenable to static analysis

– Deadlocks “typically” become repeatable

– “Beautiful” (declarative) code

© A

SE

RT

2006-2

013

import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task final flow = new Dataflows() task { flow.x = flow.y } task { flow.y = flow.x }

Page 76: functional groovy

…GPars: Actors...

© A

SE

RT

2006-2

013

import static groovyx.gpars.actor.Actors.* def votes = reactor { it.endsWith('y') ? "You voted for $it" : "Sorry, please try again" } println votes.sendAndWait('Groovy') println votes.sendAndWait('JRuby') println votes.sendAndWait('Go') def languages = ['Groovy', 'Dart', 'C++'] def booth = actor { languages.each{ votes << it } loop { languages.size().times { react { println it } } stop() } } booth.join(); votes.stop(); votes.join()

You voted for Groovy

You voted for JRuby

Sorry, please try again

You voted for Groovy

Sorry, please try again

Sorry, please try again

Page 77: functional groovy

Software Transactional Memory…

© A

SE

RT

2006-2

013

@Grab('org.multiverse:multiverse-beta:0.7-RC-1') import org.multiverse.api.references.LongRef import static groovyx.gpars.stm.GParsStm.atomic import static org.multiverse.api.StmUtils.newLongRef class Account { private final LongRef balance Account(long initial) { balance = newLongRef(initial) } void setBalance(long newBalance) { if (newBalance < 0) throw new RuntimeException("not enough money") balance.set newBalance } long getBalance() { balance.get() } } // ...

Page 78: functional groovy

…Software Transactional Memory

© A

SE

RT

2006-2

013

// ... def from = new Account(20) def to = new Account(20) def amount = 10 def watcher = Thread.start { 15.times { atomic { println "from: ${from.balance}, to: ${to.balance}" } sleep 100 } } sleep 150 try { atomic { from.balance -= amount to.balance += amount sleep 500 } println 'transfer success' } catch(all) { println all.message } atomic { println "from: $from.balance, to: $to.balance" } watcher.join()

Page 79: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

• GPars & Concurrency

Type Safety

• Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 80: functional groovy

Show me the code

JScience, SPrintfChecker, GenericStackTest

Page 81: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

• GPars & Concurrency

• Type Safety

Word Split (bonus material)

• More Info

© A

SE

RT

2006-2

013

Page 82: functional groovy

Word Split with Fortress

© A

SE

RT

2006-2

013

Guy Steele’s StrangeLoop keynote (from slide 52 onwards for several slides):

http://strangeloop2010.com/talk/presentation_file/14299/GuySteele-parallel.pdf

Page 83: functional groovy

Word Split…

© A

SE

RT

2006-2

013

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']

Page 84: functional groovy

Word Split…

© A

SE

RT

2006-2

013

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 }

Page 85: functional groovy

Word Split…

© A

SE

RT

2006-2

013

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 }

Page 86: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

Page 87: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

Page 88: functional groovy

Segment(left1, m1, right1) Segment(left2, m2, right2)

Segment(left1, m1 + [ ? ] + m2, right2)

…Word Split…

© A

SE

RT

2006-2

013

Page 89: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

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) } }

Page 90: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

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']

Page 91: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

Page 92: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

Page 93: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

THREADS = 4 def pwords(s) { int n = (s.size() + THREADS - 1) / THREADS def map = new ConcurrentHashMap() (0..<THREADS).collect { i -> Thread.start { def (min, max) = [ [s.size(), i * n].min(), [s.size(), (i + 1) * n].min() ] map[i] = swords(s[min..<max]) } }*.join() (0..<THREADS).collect { i -> map[i] }.sum().flatten() }

Page 94: functional groovy

…Word Split…

© A

SE

RT

2006-2

013

import static groovyx.gpars.GParsPool.withPool THRESHHOLD = 10 def partition(piece) { piece.size() <= THRESHHOLD ? piece : [piece[0..<THRESHHOLD]] + partition(piece.substring(THRESHHOLD)) } def pwords = { input -> withPool(THREADS) { partition(input).parallel.map(swords).reduce{ a, b -> a + b }.flatten() } }

Page 95: functional groovy

…Guy Steele example in Groovy…

© A

SE

RT

2006-2

013

def words = { s -> int n = (s.size() + THREADS - 1) / THREADS def min = (0..<THREADS).collectEntries{ [it, [s.size(),it*n].min()] } def max = (0..<THREADS).collectEntries{ [it, [s.size(),(it+1)*n].min()] } def result = new DataFlows().with { task { a = swords(s[min[0]..<max[0]]) } task { b = swords(s[min[1]..<max[1]]) } task { c = swords(s[min[2]..<max[2]]) } task { d = swords(s[min[3]..<max[3]]) } task { sum1 = a + b } task { sum2 = c + d } task { sum = sum1 + sum2 } println 'Tasks ahoy!' sum } switch(result) { case Chunk: return maybeWord(result.s) case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) } } }

DataFlow version: partially hard-coded to 4 partitions for easier reading

Page 96: functional groovy

…Guy Steele example in Groovy…

© A

SE

RT

2006-2

013

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..<last]) } else { // divide and conquer def mid = first + ((last - 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

Page 97: functional groovy

…Guy Steele example in Groovy

© A

SE

RT

2006-2

013

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

Page 98: functional groovy

Topics

• Intro to Functional Style

• Functional Basics

• Immutability & Persistent Data Structures

• Laziness & Strictness

• GPars & Concurrency

• Type Safety

• Word Split (bonus material)

More Info

© A

SE

RT

2006-2

013

Page 99: functional groovy

More Information: Groovy in Action