Successfully reported this slideshow.

Discovering functional treasure in idiomatic Groovy

5

Share

Upcoming SlideShare
Groovy unleashed
Groovy unleashed
Loading in …3
×
1 of 42
1 of 42

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Discovering functional treasure in idiomatic Groovy

  1. 1. Discovering Functional Treasure in Idiomatic Groovy Naresha K Enteleki Solutions ! naresha.k@gmail.com @naresha_k
  2. 2. An imperative language on JVM A dynamic with Functional Flavour
  3. 3. The origin http://radio-weblogs.com/0112098/2003/08/29.html initial idea was to make a little dynamic language which compiles directly to Java classes and provides all the nice (alleged) productivity benefits - James Strachan
  4. 4. Prerequisites
  5. 5. Function ! def sayHello(){! ! println 'Hello'! }! ! sayHello()!
  6. 6. Closure def wish = {! ! println "Hello"! }! ! wish() def wishFriend = {! ! println "Hello $it"! } ! ! wishFriend 'Raj' def wishFriend = { to ->! ! println "Hello $to"! }! ! wishFriend 'Raj'
  7. 7. Closure - No Arg def wish = { ->! ! println "Hello"! }! ! wish()
  8. 8. Closure - Multiple args def wishWithMessage = { to, message ->! ! println "Hello $to, $message"! }! ! wishWithMessage "Raj", "Good Evening"
  9. 9. Closures = Power functions def wishWithMessage = { to, message ->! ! println "Hello $to, $message"! }! ! wishWithMessage "Raj", "Good Evening" def <var> = <closure> Functions as Values
  10. 10. Sample Data import groovy.transform.ToString! ! @ToString(includeNames=true)! class Geek{! String name! int age! List<String> languages! } def geeks = []! geeks << new Geek(name: 'Raj', age: 24, ! ! languages: ['Java', 'Groovy'])! geeks << new Geek(name: 'Arun', age: 35, ! ! languages: ['Java', 'Scala', 'Clojure'])! geeks << new Geek(name: 'Kumar', age: 28, ! ! languages: ['Groovy', 'Scala'])!
  11. 11. Geeks who can speak Groovy def findGroovyGeeksImperative(geeks){! ! def groovyGeeks = []! ! for(geek in geeks){! ! if(geek.languages.contains('Groovy')){! ! groovyGeeks << geek! ! }! ! }! ! groovyGeeks! }
  12. 12. Geeks who can speak Groovy def findGroovyGeeksImperative(geeks){! ! def groovyGeeks = []! ! for(geek in geeks){! ! if(geek.languages.contains('Groovy')){! ! groovyGeeks << geek! ! }! ! }! ! groovyGeeks! }
  13. 13. Generalised def findGeeks(geeks, String language){! ! def knowsLang = []! ! for(geek in geeks){! ! if(geek.languages.contains(language)){! ! knowsLang << geek! ! }! ! }! ! knowsLang! }!
  14. 14. Towards Idiomatic Groovy def findGroovyGeeksFunctional(geeks){! ! geeks.findAll({it.languages.contains('Groovy')})! } def findGroovyGeeksFunctional(geeks){! ! geeks.findAll() {it.languages.contains('Groovy')}! } def findGroovyGeeksFunctional(geeks){! ! geeks.findAll {it.languages.contains('Groovy')}! }
  15. 15. Reusable def knowsGroovy = { geek -> ! ! geek.languages.contains('Groovy')! }! ! def findGeeksFunctional(geeks, criterion){! ! geeks.findAll(criterion)! }! ! println findGeeksFunctional(geeks, knowsGroovy) Higher Order Functions
  16. 16. Strategy Pattern def knowsGroovy = { geek -> ! ! geek.languages.contains('Groovy')! }! ! def knowsClojure = { geek ->! ! geek.languages.contains('Clojure')! }! ! def findGeeksFunctional(geeks, criterion){! ! geeks.findAll(criterion)! }! ! println findGeeksFunctional(geeks, knowsGroovy)! println findGeeksFunctional(geeks, knowsClojure)
  17. 17. Command Pattern def sayHello = {! ! println "Hello"! }! ! def sayHi = {! ! println "Hi"! }! ! [sayHello, sayHi].each{ command ->! ! command()! }
  18. 18. Execute Around def sayHello = {! ! println "Hello"! }! ! def sayHi = {! ! println "Hi"! }! ! [sayHello, sayHi].each{ command ->! ! println "Before Command"! ! command()! ! println "After Command"! }
  19. 19. Code Smell! def knowsGroovy = { geek -> ! ! geek.languages.contains('Groovy')! }! ! def knowsClojure = { geek ->! ! geek.languages.contains('Clojure')! }
  20. 20. After DRYing def knowsLanguage = { geek, language ->! ! geek.languages.contains(language)! }! ! def findGeeks(geeks, criterion, String language){! ! geeks.findAll {criterion(it, language)}! }! ! println findGeeks(geeks, knowsLanguage, 'Groovy')
  21. 21. A Better Approach def knowsLanguage = { geek, language ->! ! geek.languages.contains(language)! }! ! def knowsGroovy = knowsLanguage.rcurry('Groovy')! def knowsClojure = knowsLanguage.rcurry('Clojure')! ! def findGeeks(geeks, criterion){! ! geeks.findAll(criterion)! }! ! println findGeeks(geeks, knowsGroovy)! println findGeeks(geeks, knowsClojure) Curried Functions
  22. 22. Geeks • Knows Groovy • At least 25 years old
  23. 23. Composing em def atleast25YearsOld = { geek ->! ! geek.age >= 25! }! ! def findGeeks(geeks, criterion){! ! geeks.findAll(criterion)! }! ! def findGroovyGeeks = (this.&findGeeks)! ! .rcurry(knowsGroovy)! def findGeeksAtLeast25 = (this.&findGeeks)! ! .rcurry(atleast25YearsOld) def findGroovyGeeksOlderThan24 = ! ! findGeeksAtLeast25 << findGroovyGeeks! ! println findGroovyGeeksOlderThan24(geeks) Function Composition
  24. 24. Towards Immutable Data def geeks = []! geeks << new Geek(name: 'Raj', age: 24, ! ! languages: ['Java', 'Groovy'])! geeks << new Geek(name: 'Arun', age: 35, ! ! languages: ['Java', 'Scala', 'Clojure'])! geeks << new Geek(name: 'Kumar', age: 28, ! ! languages: ['Groovy', 'Scala']) geeks2 = geeks + new Geek(name: 'Mark', age: 40, ! ! languages: ['Lisp', 'Haskell']) geeksImmutable = geeks.asImmutable()
  25. 25. Towards Immutable Data … def geeksOrderedByAge = geeks.sort { it.age }! println geeksOrderedByAge! println geeks def geeksOrderedByAge = geeks.sort false, { it.age }! println geeksOrderedByAge! println geeks Pure Functions
  26. 26. Demo
  27. 27. import groovy.transform.*! ! @TailRecursive! def factorial(number, fact = 1){! ! number == 0 ? fact : factorial(number - 1, fact * number)! }! ! println factorial(2500G) Tail Call Optimization
  28. 28. Prior to Groovy 2.3 def fact! fact = { number, result ->! ! number == 0 ? result : ! ! ! fact.trampoline(number-1, result * number)! }.trampoline()
  29. 29. Slow Functions
  30. 30. import groovy.transform.*! ! @Memoized! def timeConsumingOperation(int number){! ! println "Performing computation"! ! number * number! }! ! println timeConsumingOperation(2)! println timeConsumingOperation(2) Memoization
  31. 31. Map Filter Reduce println geeks.findAll { it.languages.contains('Groovy')}! ! ! ! .collect {it.age}! ! ! ! .sum()! ! println geeks.findAll { it.languages.contains('Groovy')}! ! ! ! .collect {it.age}! ! ! ! .with{ sum()/ size()}
  32. 32. Defer
  33. 33. Defer import groovy.transform.*! ! class Website{! ! String address! ! @Lazy ! ! URL url = address.toURL()! }! ! ! def fuconf = new Website(address: 'http://functionalconf.com/')! println fuconf.dump()! ! def content = fuconf.url.text! println content.grep("n").size()! println fuconf.dump()! Lazy Evaluation
  34. 34. Recursion vs Iteration
  35. 35. Recursion vs Iteration def ages = geeks.collect { it.age }! ! def sum! sum = { head, tail ->! ! if(!tail){! ! ! head! ! }! ! else{! ! ! head + sum(tail.head(), tail.tail())! ! }! }! ! println(sum(0, ages))
  36. 36. Recursion vs Iteration println ages.inject(0) { s, item ->! ! s + item! }
  37. 37. The Ultimate Lesson https://twitter.com/mfeathers/status/29581296216
  38. 38. Functional Treasures Functions as values (First class citizens) Higher order functions Curried Functions Function Composition Pure Functions (Immutability) Tail Call Optimization Memoization Lazy Evaluation
  39. 39. Welcome to
  40. 40. References • https://github.com/naresha/functionalconf2014

×