Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

What's in Groovy for Functional Programming

99 views

Published on

Slides from my APACHECON@HOME 2020 talk - "What's in Groovy for Functional Programming".

The directions in which popular programming languages are heading to is clear evidence of the need for multiple programming paradigms. One such programming paradigm that is gaining attention these days is functional programming. Groovy too has embraced functional programming and provides a wide variety of features for a developer to code in the functional style. In this session, I demonstrate the functional programming features of Groovy. We start with the higher-order function support in Groovy and see the benefits they offer. From the example, we can observe that functional programming is indeed idiomatic in several parts of Groovy. We then step into implementing functional composition, currying, memoizing tail-call optimization, and recursion.

Published in: Software
  • Be the first to comment

  • Be the first to like this

What's in Groovy for Functional Programming

  1. 1. What’s in Groovy for Functional Programming Naresha K @naresha_k https://blog.nareshak.com/ APACHECON @HOME Spt, 29th – Oct. 1st 2020
  2. 2. About me Developer, Architect & Tech Excellence Coach Founder & Organiser Bangalore Groovy User Group 2
  3. 3. https://twitter.com/mfeathers/status/29581296216 3
  4. 4. 4
  5. 5. String greet(String message) { "Hello, ${message}" } String greet() { "Hello" } String greet(String message, String friend) { "Hello $friend, $message" } Regular Functions 5
  6. 6. String greet(String message) { "Hello, ${message}" } def greet = { message -> "Hello, ${message}" } // OR def greet = { "Hello, ${it}" } println greet("Good morning") Closures are powerful 6
  7. 7. String greet() { "Hello" } def greet = { -> "Hello" } println greet() 7
  8. 8. String greet(String message, String friend) { "Hello $friend, $message" } def greet = { message, friend -> "Hello $friend, $message" } println greet() 8
  9. 9. def greet = { message -> "Hello, ${message}" } def message = "Hello" Integer age = 30 println greet("Good morning") Closures are first class 9
  10. 10. @ToString(includePackage = false) class Developer { String name int age List<String> skills } List<Developer> developers = [ new Developer(name: 'Raj', age: 24, skills: ['Java', 'Groovy']), new Developer(name: 'Sheldon', age: 30, skills: ['Java', 'Scala', 'Clojure']), new Developer(name: 'Penny', age: 28, skills: ['Groovy', 'Scala']), ] Sample data 10
  11. 11. List<Developer> findGroovyDevelopers(List<Developer> devs) { List<Developer> groovyDevs = [] for(developer in devs) { if(developer.skills.contains("Groovy")){ groovyDevs << developer } } groovyDevs } println findGroovyDevelopers(developers) Imperative code! 11
  12. 12. List<Developer> findGroovyDevelopers(List<Developer> devs) { List<Developer> groovyDevs = [] for(developer in devs) { if(developer.skills.contains("Groovy")){ groovyDevs << developer } } groovyDevs } List<Developer> findGroovyDevelopers(List<Developer> devs) { devs.findAll { dev -> dev.skills.contains('Groovy') } } Refactor to idiomatic Groovy 12
  13. 13. List<Developer> findGroovyDevelopers(List<Developer> devs) { List<Developer> groovyDevs = [] for(developer in devs) { if(developer.skills.contains("Groovy")){ groovyDevs << developer } } groovyDevs } List<Developer> findGroovyDevelopers(List<Developer> devs) { devs.findAll { dev -> dev.skills.contains('Groovy') } } Higher Order Function 13
  14. 14. List<Developer> findGroovyDevelopers(List<Developer> devs) { devs.findAll { dev -> dev.skills.contains('Groovy') } } List<Developer> findGroovyDevelopers(List<Developer> devs) { def knowsGroovy = { dev -> dev.skills.contains('Groovy') } devs.findAll(knowsGroovy) } List<Developer> findDevelopers(List<Developer> devs, Closure skillFinder) { devs.findAll(skillFinder) } 14
  15. 15. List<Developer> findDevelopers(List<Developer> devs, Closure skillFinder) { devs.findAll(skillFinder) } Closure createSkillFinder(String language) { Closure skillFinder = { Developer developer -> developer.skills.contains(language) } skillFinder } Closure knowsGroovy = createSkillFinder('Groovy') println findDevelopers(developers, knowsGroovy) 15
  16. 16. Closure knowsGroovy = createSkillFinder('Groovy') Closure knowsJava = createSkillFinder('Java') println findDevelopers(developers, knowsGroovy) println findDevelopers(developers, knowsJava) 16
  17. 17. Closure knowsGroovy = createSkillFinder('Groovy') Closure knowsJava = createSkillFinder('Java') println findDevelopers(developers, knowsGroovy) println findDevelopers(developers, knowsJava) Strategy Pattern 17
  18. 18. def command1 = { println "Running command-1" } def command2 = { println "Running command-2" } void runCommand(Closure command) { command() } runCommand(command1) runCommand(command2) Command Pattern 18
  19. 19. def command1 = { println "Running command-1" } def command2 = { println "Running command-2" } void runCommand(Closure command) { command() } [command1, command2].each { runCommand(it) } Command Pattern 19
  20. 20. def command1 = { println "Running command-1" } def command2 = { println "Running command-2" } void runCommand(Closure command) { println "Before command" command() println "After command" } [command1, command2].each { runCommand(it) } Execute-Around Pattern 20
  21. 21. def add = { a, b -> a + b } println add(10, 20) 21
  22. 22. def add = { a, b -> a + b } println add(10, 20) def increment = add.curry(1) println increment(10) Partial Application/ Currying 22
  23. 23. Closure createSkillFinder(String language) { Closure skillFinder = { Developer developer -> developer.skills.contains(language) } skillFinder } Closure knowsGroovy = createSkillFinder('Groovy') Closure knowsJava = createSkillFinder('Java') def findDevelopers = { List<Developer> devs, Closure skillFinder -> devs.findAll(skillFinder) } 23
  24. 24. def findDevelopers = { List<Developer> devs, Closure skillFinder -> devs.findAll(skillFinder) } List<String> firstNamesOfDevs(List<Developer> devs, Closure devSelector) { List<Developer> selectedDevs = devSelector(devs) selectedDevs.collect { it.name } } 24
  25. 25. def findDevelopers = { List<Developer> devs, Closure skillFinder -> devs.findAll(skillFinder) } List<String> firstNamesOfDevs(List<Developer> devs, Closure devSelector) { List<Developer> selectedDevs = devSelector(devs) selectedDevs.collect { it.name } } def groovyDevSelector = findDevelopers.rcurry(knowsGroovy) println firstNamesOfDevs(developers, groovyDevSelector) 25
  26. 26. println firstNamesOfDevs(developers, javaAndGroovyDevSelector) 26
  27. 27. Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy) Closure javaDevSelector = findDevelopers.rcurry(knowsJava) println firstNamesOfDevs(developers, javaAndGroovyDevSelector) 27
  28. 28. Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy) Closure javaDevSelector = findDevelopers.rcurry(knowsJava) Closure javaAndGroovyDevSelector = groovyDevSelector << javaDevSelector println firstNamesOfDevs(developers, javaAndGroovyDevSelector) Function Composition 28
  29. 29. Closure knowsGroovy = { dev -> dev.skills.contains('Groovy') } Closure ageOfDev = { Developer developer -> developer.age } def averageAgeOfGroovyDevs = developers .findAll(knowsGroovy) .collect(ageOfDev) .with { sum() / size() } Map, filter, reduce 29
  30. 30. Closure knowsGroovy = { dev -> dev.skills.contains('Groovy') } Closure ageOfDev = { Developer developer -> developer.age } def averageAgeOfGroovyDevs = developers.stream() .filter(knowsGroovy) .map(ageOfDev) .mapToInt(number -> number) .average() .orElse(0) Streams API compatibility 30
  31. 31. Pure Functions and Immutable Data 31
  32. 32. // Instead of developers.sort() // Use sortedData = developers.sort(false) 32
  33. 33. developers.asImmutable() 33
  34. 34. @Immutable class Point { int x int y } new Point(x: 10, y: 20) public final class Point { private final int x private final int y public Point(int x, int y) { // } public Point(java.util.Map args) { // } public Point() { this ([:]) } public final int getX() { return x } public final int getY() { return y } } 34
  35. 35. def numbers = [1, 2, 3, 4, 5] println numbers.inject(0) { s, item -> s + item } Iteration/ fold left 35
  36. 36. def numbers = [1, 2, 3, 4, 5] def sum sum = { head, tail -> if (!tail) { head } else { head + sum(tail.head(), tail.tail()) } } println(sum(0, numbers)) Recursion 36
  37. 37. import groovy.transform.* @TailRecursive def factorial(number, fact = 1) { number == 0 ? fact : factorial(number - 1, fact * number) } println factorial(2500G) Tail call optimisation 37
  38. 38. import groovy.transform.* @TailRecursive def factorial(number, fact = 1) { number == 0 ? fact : factorial(number - 1, fact * number) } println factorial(2500G) // factorial(3,1) // factorial(2, 1 * 3) // factorial(1, 1 * 3 * 2) // factorial(0, 1 * 3 * 2 * 1) // 1 * 3 * 2 * 1 // 6 38
  39. 39. import groovy.transform.Memoized int timeConsumingOp(int number) { Thread.sleep(5000) number * number } println timeConsumingOp(10) println timeConsumingOp(10) println timeConsumingOp(10) 39
  40. 40. import groovy.transform.Memoized @Memoized int timeConsumingOp(int number) { Thread.sleep(5000) number * number } println timeConsumingOp(10) println timeConsumingOp(10) println timeConsumingOp(10) Memoizing 40
  41. 41. https://blog.nareshak.com/whats-in-groovy-for-functional-programming/ 41
  42. 42. Effective Java With Groovy - How Language Influences Adoption of Good Practices APACHECON @HOME Spt, 29th – Oct. 1st 2020 42
  43. 43. Thank You APACHECON @HOME Spt, 29th – Oct. 1st 2020 43

×