functional programming with groovy

71
Functional Programming with Groovλ

Upload: arturo-herrero

Post on 04-Dec-2014

12.418 views

Category:

Technology


4 download

DESCRIPTION

My talk about Functional Programming with Groovy at Greach Greach http://greach.es/ the Groovy spanish conf Date: 04-11-2011

TRANSCRIPT

Page 1: Functional Programming with Groovy

Functional Programmingwith Groovλ

Page 2: Functional Programming with Groovy

@ArturoHerrero

http://arturoherrero.com

Page 3: Functional Programming with Groovy

Working at OSOCO

Small but outstanding software development shop

Groovy and Grails hackers

on EC2 cloud nine

TDD mantra singers

Quality preachers

Page 4: Functional Programming with Groovy

(define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1)))))

LISt Processing

Page 5: Functional Programming with Groovy

( (factorial ) ( ( ) ( (factorial ( )))))

Lots of Insipid Stupid Parentheses

Page 6: Functional Programming with Groovy

Code Complete

The Pragmatic Programmer

Structure and Interpretation of Computer Programs

Page 8: Functional Programming with Groovy

A language that doesn't affect the way you think

about programming, is not worth knowing

Alan Perlis

Page 9: Functional Programming with Groovy

Functional Programming

Page 10: Functional Programming with Groovy

Function

Page 11: Functional Programming with Groovy

Functional Programming

Avoiding Mutable State Side-Effect-Free Functions Referential Transparency First-Class Citizens Higher-Order Functions Lambdas and Closures Lazy Evaluation Recursion

λλ

λλ

λλ

λλ

Page 12: Functional Programming with Groovy

Why Functional Programming?

Referential transparency Unit testing Debbuging Parallelization Modularity and composition Increases the quality of code Abstractions

λλ

λλ

λλ

λ

Page 14: Functional Programming with Groovy

Imperative vs. Declarative

Imperative: how to achieve our goal Take the next customer from a list. If the customer lives in Spain, show their details. If there are more customers in the list, go to the beginning

Declarative: what we want to achieve Show customer details of every customer living in Spain

Page 15: Functional Programming with Groovy

Imperative vs. Declarative

Functional programming is like describing your problem to a mathematician.

Imperative programming is like giving instructions to an idiot.

arcus, #scheme on Freenode

Page 16: Functional Programming with Groovy

Functional Programmingwith Groovy?

is an imperative language,

but we still can apply functional principles

It's basically a programmer's choice

Page 17: Functional Programming with Groovy

Immutability

Simple Immutable objects can only be in exactly one state, the state in which it was created

Always consistent Less prone to errors and more secure

Immutable objects can be shared freely Freedom to cache

Inherently thread-safe

Page 18: Functional Programming with Groovy

Immutability

NO: (even being a name rebind and not a real update)

book = 'Fooled by Randomness'book = "$book - Nassim Taleb"book = "$book (2001)"assert 'Fooled by Randomness - Nassim Taleb (2001)' == book

YES:book = 'Fooled by Randomness'bookWithAuthor = "$book - Nassim Taleb"completeBook = "$bookWithAuthor (2001)"assert 'Fooled by Randomness - Nassim Taleb (2001)' == completeBook

Page 19: Functional Programming with Groovy

Immutability

NO:years = [2001, 2002]years << 2003years += [2004, 2005]assert [2001, 2002, 2003, 2004, 2005] == years

YES:years = [2001, 2002]allYears = years + 2003 + [2004, 2005]assert [2001, 2002, 2003, 2004, 2005] == allYears

Page 20: Functional Programming with Groovy

Immutability

def list = ['Gr', 'vy']

NO:

list.addAll 1, 'oo'assert list == ['Gr', 'oo', 'vy']

YES:

assert list.plus(1, 'oo') == ['Gr', 'oo', 'vy']assert list == ['Gr', 'vy']

Page 21: Functional Programming with Groovy

Immutability

def list = [1, 2, 2, 3]

NO:

list.removeAll 2assert list == [1, 3]

YES:

assert list.minus(2) == [1, 3]assert list == [1, 2, 2, 3]

Page 22: Functional Programming with Groovy

Immutability

def list = ['Scala', 'Groovy', 'Java']

NO:

sortedList = list.sort()assert sortedList == ['Groovy', 'Java', 'Scala']assert list == ['Groovy', 'Java', 'Scala']

YES:

sortedList = list.sort(false)assert sortedList == ['Groovy', 'Java', 'Scala']assert list == ['Scala', 'Groovy', 'Java']

Page 23: Functional Programming with Groovy

Immutability

def list = ['Java', 'Groovy', 'Java']

NO:

uniqueList = list.unique()assert uniqueList == ['Java', 'Groovy']assert list == ['Java', 'Groovy']

YES:

uniqueList = list.unique(false)assert uniqueList == ['Java', 'Groovy']assert list == ['Java', 'Groovy', 'Java']

Page 24: Functional Programming with Groovy

Immutability

def list = ['Java', 'Groovy']

NO:

reverseList = list.reverse(true)assert reverseList == ['Groovy', 'Java']assert list == ['Groovy', 'Java']

YES:

reverseList = list.reverse()assert reverseList == ['Groovy', 'Java']assert list == ['Java', 'Groovy']

Page 25: Functional Programming with Groovy

Immutability Collection

def list = ['Groovy', 'Java'].asImmutable()assert 'Groovy' == list.first()

try { list.add 'Scala' // Cannot add item} catch (e) { assert e instanceof UnsupportedOperationException}

try { list.remove 'Java' // Cannot remove item} catch (e) { assert e instanceof UnsupportedOperationException}

Page 26: Functional Programming with Groovy

Immutability Class

@Immutable class Coordinates { Double latitude, longitude }

def c1 = new Coordinates(latitude: 48.824068, longitude: 2.531733)

def c2 = new Coordinates(48.824068, 2.531733)

assert c1 == c2

Page 27: Functional Programming with Groovy

Higher-Order Functions

First-Class Citizen Can be stored in variables Can be passed as function parameter Can be returned from functions

Higher-Order Functions (First-Class Functions)

Functions that take other functions as arguments or return them as results

Page 28: Functional Programming with Groovy

Closures

def closure = { 'Hello world!' } assert closure() == 'Hello world!'

def sum = { a, b -> a + b } assert sum(2,3) == 5

def square = { it * it } assert square(9) == 81

final BASE = 1000 def salary = { variable -> BASE + variable } assert salary(500) == 1500

Page 29: Functional Programming with Groovy

Turn Methods into Closures

def salary(variable) { final BASE = 1000 BASE + variable } assert salary(500) == 1500

def salaryClosure = this.&salary assert salaryClosure(500) == 1500

Page 30: Functional Programming with Groovy

Closures Composition

def minutesToSeconds = { it * 60 }def hoursToMinutes = { it * 60 }def daysToHours = { it * 24 } def hoursToSeconds = minutesToSeconds << hoursToMinutesdef daysToSeconds = hoursToSeconds << daysToHours

assert daysToSeconds(1) == 86400

Page 31: Functional Programming with Groovy

Closures Composition

def upper = { it.toUpperCase() }def firstLetter = { it.charAt(0) }

def words = ["Don't", "Repeat", "Yourself"]def acronym = words.collect(firstLetter >> upper).join()

assert acronym == 'DRY'

Page 32: Functional Programming with Groovy

Currying

given: ƒ: (X x Y) -> Z

then: curry(ƒ): X -> (Y -> Z)

Takes a function with a particularnumber of parameters and returns afunction with some of the parametervalues fixed, creating a new function

Page 33: Functional Programming with Groovy

Currying

def modulus = { mod, num -> num % mod } assert modulus(2, 5) == 1 assert modulus(3, 5) == 2

def mod2 = modulus.curry(2) assert mod2(5) == 1 def mod3 = modulus.curry(3) assert mod3(5) == 2

Page 34: Functional Programming with Groovy

Currying

def bill = { amount, currency -> "$amount $currency" } assert bill(1000, '$') == '1000 $' assert bill(1000, '€') == '1000 €'

def billInDollars = bill.rcurry('$') assert billInDollars(1000) == '1000 $'

def billInEuros = bill.rcurry('€') assert billInEuros(1000) == '1000 €'

Page 35: Functional Programming with Groovy

Currying

def joinWithSeparator = { one, sep, two -> one + sep + two }

def joinWithAmpersand = joinWithSeparator.ncurry(1, '&')

assert joinWithAmpersand('a', 'b') == 'a&b'

Page 36: Functional Programming with Groovy

Classic Operations on Functional Data Types

list filter

map

fold

Page 37: Functional Programming with Groovy

Classic Operations on Functional Data Types

list findAll

collect

inject

Page 38: Functional Programming with Groovy

Classic Operations on Functional Data Types

list any

sort

sum

every

min

Page 39: Functional Programming with Groovy

findAll()NO:

def result = [][1, 2, 3, 4].each { if (it > 2) { result << it }}assert result == [3, 4]

YES:

assert [1, 2, 3, 4].findAll{ it > 2 } == [3, 4]

Page 40: Functional Programming with Groovy

collect()NO:

def result = [][1, 2, 3].each { result << it * 2}assert result == [2, 4, 6]

YES:

assert [1, 2, 3].collect{ it * 2 } == [2, 4, 6]

Page 41: Functional Programming with Groovy

inject()NO:

def total = 0[1, 2, 3].each { total += it}assert total == 6

YES:

def total = [1, 2, 3].inject(0) { acc, n -> acc + n}assert total == 6

Page 42: Functional Programming with Groovy

find()NO:

def resulttry { [1, 2, 3].each { if (it > 1) { result = it throw new Exception() // monstrous } }} catch(exception) { }assert result == 2

YES:

assert [1, 2, 3].find{ it > 1 } == 2

Page 43: Functional Programming with Groovy

max() @TupleConstructor // import groovy.transform.* class Person { String name Integer age }

def person1 = new Person('Arturo', 26) def person2 = new Person('Luis', 61) def person3 = new Person('Laura', 19) def family = [] << person1 << person2 << person3

assert family.max{ it.age }.age == 61 assert family.collect{ it.age }.max() == 61 assert family*.age.max() == 61

Page 44: Functional Programming with Groovy

Refactoring def exists = false family.each { person -> if (person.age > 60) { exists = true } } assert exists == true

def exists = family.inject(false) { found, person -> if (person.age > 60) { found = true } return found } assert exists == true

assert family.any{ it.age > 60 } == true

Page 45: Functional Programming with Groovy

@TupleConstructor // import groovy.transform.* class Person { String name String lastname Integer age }

def rafa = new Person('Rafael', 'Luque', 36) def marcin = new Person('Marcin', 'Gryszko', 34) def arturo = new Person('Arturo', 'Herrero', 26) def osokers = [] << rafa << marcin << arturo << rafa assert osokers.unique(false) .findAll{ it.age > 30} .sort{ it.lastname } == [marcin, rafa]

assert osokers == [rafa, marcin, arturo, rafa]

Combinator Functions

Page 46: Functional Programming with Groovy

Combinator Functions // Procedural style def count = 0 for (i in (1 .. 1000)) { if (i % 2) { count += ("$i".size()) } } assert count == 1445

// Functional style def count = (1 .. 1000).findAll{ it % 2 } .collect{ "$it" } .inject(0) { sum, num -> sum + num.size() } assert count == 1445

Page 47: Functional Programming with Groovy

Lazy Evaluation

Only does as much work as necessary Delays the evaluation of the expression until it's needed

CPU efficient The value is not calculated or assigned until the value is requested

Manage potentially infinite data structures Only a manageable subset of the data will actually be used

Page 48: Functional Programming with Groovy

Lazy Evaluation

class Person { @Lazy String name = 'Arturo' } def person = new Person() assert !(person.dump().contains('Arturo'))

assert person.name.size() == 6 assert person.dump().contains('Arturo')

Page 49: Functional Programming with Groovy

Lazy Evaluation

class Get { String url @Lazy URL urlObj = { url?.toURL() }() @Lazy(soft=true) String text = urlObj?.text }

def get = new Get(url: 'http://arturoherrero.com') assert get.url == 'http://arturoherrero.com' assert get.dump().contains('text=null') assert get.dump().contains('urlObj=null')

assert get.urlObj.protocol == 'http' assert get.urlObj.host == 'arturoherrero.com' assert get.text.contains('Arturo Herrero')

Page 50: Functional Programming with Groovy

Lazy Evaluation

groovy.sql.DataSet.DataSet groovy.util.XmlSlurper

@Singleton(lazy=true) class Util { Integer count(text) { text.size() } } assert 6 == Util.instance.count('Arturo')

Page 51: Functional Programming with Groovy

Infinite structures

class LazyList { ...}

def naturalnumbers = integers(1)assert '1 2 3 4 5 6' == naturalnumbers.take(6).join(' ')

def evennumbers = naturalnumbers.filter{ it % 2 == 0 }assert '2 4 6 8 10 12' == evennumbers.take(6).join(' ')

Page 52: Functional Programming with Groovy

Infinite structures

@Grab('org.functionaljava:functionaljava:3.0')import fj.data.Stream

Stream.metaClass.filter = { Closure c -> delegate.filter(c as fj.F) }

Stream.metaClass.asList = { delegate.toCollection().asList() }

def evens = Stream.range(1).filter{ it % 2 == 0 }assert [2, 4, 6, 8, 10, 12] == evens.take(6).asList()

Page 53: Functional Programming with Groovy

Recursive factorial(6) 6 * factorial(5) 6 * (5 * factorial(4)) 6 * (5 * (4 * factorial(3))) 6 * (5 * (4 * (3 * factorial(2)))) 6 * (5 * (4 * (3 * (2 * factorial(1))))) 6 * (5 * (4 * (3 * (2 * 1)))) 6 * (5 * (4 * (3 * 2))) 6 * (5 * (4 * 6)) 6 * (5 * 24) 6 * 120 720

Page 54: Functional Programming with Groovy

Recursive factorial(6) 6 * factorial(5) 6 * (5 * factorial(4)) 6 * (5 * (4 * factorial(3))) 6 * (5 * (4 * (3 * factorial(2)))) 6 * (5 * (4 * (3 * (2 * factorial(1))))) 6 * (5 * (4 * (3 * (2 * 1)))) 6 * (5 * (4 * (3 * 2))) 6 * (5 * (4 * 6)) 6 * (5 * 24) 6 * 120 720

Page 55: Functional Programming with Groovy

Tail-Recursive factorial(6, 1) factorial(5, 6) factorial(4, 30) factorial(3, 120) factorial(2, 360) factorial(1, 720)

Page 56: Functional Programming with Groovy

Tail-Recursive factorial(6, 1) factorial(5, 6) factorial(4, 30) factorial(3, 120) factorial(2, 360) factorial(1, 720)

Page 57: Functional Programming with Groovy

Tail Call Optimization

3 techniques:

The compiler transform the recursion into a loop

Let the JVM recognize the recursion and eliminate it

Transform the recursion into iterative by hand

λλ

λ

Page 58: Functional Programming with Groovy

Tail Call Optimization

3 techniques:

The compiler transform the recursion into a loop

Let the JVM recognize the recursion and eliminate it

Transform the recursion into iterative by hand

λλ

λ

really?

Page 59: Functional Programming with Groovy

Tail Call Optimization

def factorial

factorial = { n -> n == 1 ? 1 : n * factorial(n - 1) }

factorial(1000)

Page 60: Functional Programming with Groovy

Tail Call Optimization

def factorial

factorial = { n -> n == 1 ? 1 : n * factorial(n - 1) }

factorial(1000)

Stack Overflow

Page 61: Functional Programming with Groovy

Tail Call Optimization

def factorial

factorial = { n, BigInteger acc = 1 -> n == 1 ? acc : factorial(n - 1, n * acc) }

factorial(1000)

Page 62: Functional Programming with Groovy

Tail Call Optimization

def factorial

factorial = { n, BigInteger acc = 1 -> n == 1 ? acc : factorial(n - 1, n * acc) }

factorial(1000)

Stack Overflow

Page 63: Functional Programming with Groovy

Tail Call Optimization

def factorial

factorial = { n, BigInteger acc = 1 -> n == 1 ? acc : factorial.trampoline(n - 1, n * acc) }.trampoline()

factorial(1000)

Page 64: Functional Programming with Groovy

Trampolining

def even, odd

even = { x -> x == 0 ? true : odd.trampoline(x - 1) }.trampoline()

odd = { x -> x == 0 ? false : even.trampoline(x - 1) }.trampoline()

assert even(1000) == true

Page 65: Functional Programming with Groovy

Memoization

def fibonacci

fibonacci = { n -> n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) }

fibonacci(35) // 9.935 seconds

Page 66: Functional Programming with Groovy

Memoization

def fibonacci

fibonacci = { n -> n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) }.memoize()

fibonacci(35) // 0.002 seconds

Page 67: Functional Programming with Groovy

def plus = { a, b -> sleep 1000; a + b }.memoize()

assert plus(1, 2) == 3 // after 1000ms assert plus(1, 2) == 3 // return immediately assert plus(2, 2) == 4 // after 1000ms assert plus(2, 2) == 4 // return immediately def plusAtLeast = { ... }.memoizeAtLeast(10) def plusAtMost = { ... }.memoizeAtMost(10) def plusBetween = { ... }.memoizeBetween(10, 20)

Memoization

Page 68: Functional Programming with Groovy

“Functional” is more a way of thinkingthan a tool set

Neal Ford

Page 69: Functional Programming with Groovy

Be a craftsman

Page 71: Functional Programming with Groovy

Thank you! @ArturoHerrero

Join us at OSOCO