SlideShare a Scribd company logo
What’s in Groovy for
Functional
Programming
Naresha K
@naresha_k
https://blog.nareshak.com/
APACHECON @HOME
Spt, 29th – Oct. 1st 2020
About me
Developer, Architect &
Tech Excellence Coach
Founder & Organiser
Bangalore Groovy User
Group
2
https://twitter.com/mfeathers/status/29581296216
3
4
String greet(String message) {
"Hello, ${message}"
}
String greet() {
"Hello"
}
String greet(String message, String friend) {
"Hello $friend, $message"
}
Regular Functions
5
String greet(String message) {
"Hello, ${message}"
}
def greet = { message -> "Hello, ${message}" }
// OR
def greet = { "Hello, ${it}" }
println greet("Good morning")
Closures are powerful
6
String greet() {
"Hello"
}
def greet = { -> "Hello" }
println greet()
7
String greet(String message, String friend) {
"Hello $friend, $message"
}
def greet = { message, friend -> "Hello $friend, $message" }
println greet()
8
def greet = { message -> "Hello, ${message}" }
def message = "Hello"
Integer age = 30
println greet("Good morning")
Closures are first class
9
@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
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
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
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
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
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
Closure knowsGroovy = createSkillFinder('Groovy')
Closure knowsJava = createSkillFinder('Java')
println findDevelopers(developers, knowsGroovy)
println findDevelopers(developers, knowsJava)
16
Closure knowsGroovy = createSkillFinder('Groovy')
Closure knowsJava = createSkillFinder('Java')
println findDevelopers(developers, knowsGroovy)
println findDevelopers(developers, knowsJava)
Strategy Pattern
17
def command1 = {
println "Running command-1"
}
def command2 = {
println "Running command-2"
}
void runCommand(Closure command) {
command()
}
runCommand(command1)
runCommand(command2)
Command Pattern
18
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
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
def add = { a, b -> a + b }
println add(10, 20)
21
def add = { a, b -> a + b }
println add(10, 20)
def increment = add.curry(1)
println increment(10)
Partial Application/ 

Currying
22
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
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
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
println firstNamesOfDevs(developers, javaAndGroovyDevSelector)
26
Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy)
Closure javaDevSelector = findDevelopers.rcurry(knowsJava)
println firstNamesOfDevs(developers, javaAndGroovyDevSelector)
27
Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy)
Closure javaDevSelector = findDevelopers.rcurry(knowsJava)
Closure javaAndGroovyDevSelector =
groovyDevSelector << javaDevSelector
println firstNamesOfDevs(developers, javaAndGroovyDevSelector)
Function Composition
28
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
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
Pure Functions
and
Immutable Data
31
// Instead of
developers.sort()
// Use
sortedData = developers.sort(false)
32
developers.asImmutable()
33
@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
def numbers = [1, 2, 3, 4, 5]
println numbers.inject(0) { s, item -> s + item }
Iteration/ fold left
35
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
import groovy.transform.*
@TailRecursive
def factorial(number, fact = 1) {
number == 0 ? fact : factorial(number - 1, fact * number)
}
println factorial(2500G)
Tail call optimisation
37
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
import groovy.transform.Memoized
int timeConsumingOp(int number) {
Thread.sleep(5000)
number * number
}
println timeConsumingOp(10)
println timeConsumingOp(10)
println timeConsumingOp(10)
39
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
https://blog.nareshak.com/whats-in-groovy-for-functional-programming/
41
Effective Java
With Groovy -
How Language
Influences Adoption of
Good Practices
APACHECON @HOME
Spt, 29th – Oct. 1st 2020
42
Thank You
APACHECON @HOME
Spt, 29th – Oct. 1st 2020
43

More Related Content

What's hot

All I know about rsc.io/c2go
All I know about rsc.io/c2goAll I know about rsc.io/c2go
All I know about rsc.io/c2goMoriyoshi Koizumi
 
プログラム実行の話と
OSとメモリの挙動の話
プログラム実行の話と
OSとメモリの挙動の話プログラム実行の話と
OSとメモリの挙動の話
プログラム実行の話と
OSとメモリの挙動の話
tatsunori ishikawa
 
OSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersOSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersLin Yo-An
 
Dip Your Toes in the Sea of Security (PHP South Africa 2017)
Dip Your Toes in the Sea of Security (PHP South Africa 2017)Dip Your Toes in the Sea of Security (PHP South Africa 2017)
Dip Your Toes in the Sea of Security (PHP South Africa 2017)
James Titcumb
 
ES2015 New Features
ES2015 New FeaturesES2015 New Features
ES2015 New Features
Giacomo Zinetti
 
Creating own language made easy
Creating own language made easyCreating own language made easy
Creating own language made easy
Ingvar Stepanyan
 
ClojurianからみたElixir
ClojurianからみたElixirClojurianからみたElixir
ClojurianからみたElixir
Kent Ohashi
 
Creating a compiler in Perl 6
Creating a compiler in Perl 6Creating a compiler in Perl 6
Creating a compiler in Perl 6
Andrew Shitov
 
Melhorando sua API com DSLs
Melhorando sua API com DSLsMelhorando sua API com DSLs
Melhorando sua API com DSLs
Augusto Pascutti
 
Climbing the Abstract Syntax Tree (PHP South Africa 2017)
Climbing the Abstract Syntax Tree (PHP South Africa 2017)Climbing the Abstract Syntax Tree (PHP South Africa 2017)
Climbing the Abstract Syntax Tree (PHP South Africa 2017)
James Titcumb
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
James Titcumb
 
c++ project on restaurant billing
c++ project on restaurant billing c++ project on restaurant billing
c++ project on restaurant billing
Swakriti Rathore
 
How To Think In Go
How To Think In GoHow To Think In Go
How To Think In Go
lestrrat
 
Hangman Game Programming in C (coding)
Hangman Game Programming in C (coding)Hangman Game Programming in C (coding)
Hangman Game Programming in C (coding)
hasan0812
 
PHP in 2018 - Q4 - AFUP Limoges
PHP in 2018 - Q4 - AFUP LimogesPHP in 2018 - Q4 - AFUP Limoges
PHP in 2018 - Q4 - AFUP Limoges
✅ William Pinaud
 
The Perl6 Type System
The Perl6 Type SystemThe Perl6 Type System
The Perl6 Type System
abrummett
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6
brian d foy
 

What's hot (20)

All I know about rsc.io/c2go
All I know about rsc.io/c2goAll I know about rsc.io/c2go
All I know about rsc.io/c2go
 
プログラム実行の話と
OSとメモリの挙動の話
プログラム実行の話と
OSとメモリの挙動の話プログラム実行の話と
OSとメモリの挙動の話
プログラム実行の話と
OSとメモリの挙動の話
 
Sbaw091006
Sbaw091006Sbaw091006
Sbaw091006
 
OSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP hatersOSDC.TW - Gutscript for PHP haters
OSDC.TW - Gutscript for PHP haters
 
Dip Your Toes in the Sea of Security (PHP South Africa 2017)
Dip Your Toes in the Sea of Security (PHP South Africa 2017)Dip Your Toes in the Sea of Security (PHP South Africa 2017)
Dip Your Toes in the Sea of Security (PHP South Africa 2017)
 
ES2015 New Features
ES2015 New FeaturesES2015 New Features
ES2015 New Features
 
Creating own language made easy
Creating own language made easyCreating own language made easy
Creating own language made easy
 
ClojurianからみたElixir
ClojurianからみたElixirClojurianからみたElixir
ClojurianからみたElixir
 
Creating a compiler in Perl 6
Creating a compiler in Perl 6Creating a compiler in Perl 6
Creating a compiler in Perl 6
 
Melhorando sua API com DSLs
Melhorando sua API com DSLsMelhorando sua API com DSLs
Melhorando sua API com DSLs
 
Climbing the Abstract Syntax Tree (PHP South Africa 2017)
Climbing the Abstract Syntax Tree (PHP South Africa 2017)Climbing the Abstract Syntax Tree (PHP South Africa 2017)
Climbing the Abstract Syntax Tree (PHP South Africa 2017)
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 
Tgh.pl
Tgh.plTgh.pl
Tgh.pl
 
c++ project on restaurant billing
c++ project on restaurant billing c++ project on restaurant billing
c++ project on restaurant billing
 
How To Think In Go
How To Think In GoHow To Think In Go
How To Think In Go
 
Hangman Game Programming in C (coding)
Hangman Game Programming in C (coding)Hangman Game Programming in C (coding)
Hangman Game Programming in C (coding)
 
PHP in 2018 - Q4 - AFUP Limoges
PHP in 2018 - Q4 - AFUP LimogesPHP in 2018 - Q4 - AFUP Limoges
PHP in 2018 - Q4 - AFUP Limoges
 
Beware sharp tools
Beware sharp toolsBeware sharp tools
Beware sharp tools
 
The Perl6 Type System
The Perl6 Type SystemThe Perl6 Type System
The Perl6 Type System
 
Learning Perl 6
Learning Perl 6 Learning Perl 6
Learning Perl 6
 

Similar to What's in Groovy for Functional Programming

Kotlin, smarter development for the jvm
Kotlin, smarter development for the jvmKotlin, smarter development for the jvm
Kotlin, smarter development for the jvm
Arnaud Giuliani
 
Lies Told By The Kotlin Compiler
Lies Told By The Kotlin CompilerLies Told By The Kotlin Compiler
Lies Told By The Kotlin Compiler
Garth Gilmour
 
Lies Told By The Kotlin Compiler
Lies Told By The Kotlin CompilerLies Told By The Kotlin Compiler
Lies Told By The Kotlin Compiler
Garth Gilmour
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
André Faria Gomes
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
Jonathan Felch
 
.gradle 파일 정독해보기
.gradle 파일 정독해보기.gradle 파일 정독해보기
.gradle 파일 정독해보기
경주 전
 
Groovy!
Groovy!Groovy!
Groovy!
Petr Giecek
 
2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws
loffenauer
 
Groovy
GroovyGroovy
Groovy
Zen Urban
 
Why Kotlin is your next language?
Why Kotlin is your next language? Why Kotlin is your next language?
Why Kotlin is your next language?
Aliaksei Zhynhiarouski
 
Introduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoIntroduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demo
Muhammad Abdullah
 
golang_getting_started.pptx
golang_getting_started.pptxgolang_getting_started.pptx
golang_getting_started.pptx
Guy Komari
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android Update
Garth Gilmour
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
Skills Matter
 
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguajeKotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Víctor Leonel Orozco López
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#
Paulo Morgado
 
PHP and MySQL
PHP and MySQLPHP and MySQL
PHP and MySQL
Sanketkumar Biswas
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with ClojureDmitry Buzdin
 
NetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf EditionNetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf Edition
Paulo Morgado
 

Similar to What's in Groovy for Functional Programming (20)

Kotlin, smarter development for the jvm
Kotlin, smarter development for the jvmKotlin, smarter development for the jvm
Kotlin, smarter development for the jvm
 
Lies Told By The Kotlin Compiler
Lies Told By The Kotlin CompilerLies Told By The Kotlin Compiler
Lies Told By The Kotlin Compiler
 
Lies Told By The Kotlin Compiler
Lies Told By The Kotlin CompilerLies Told By The Kotlin Compiler
Lies Told By The Kotlin Compiler
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
 
.gradle 파일 정독해보기
.gradle 파일 정독해보기.gradle 파일 정독해보기
.gradle 파일 정독해보기
 
Groovy!
Groovy!Groovy!
Groovy!
 
2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws
 
Groovy
GroovyGroovy
Groovy
 
Why Kotlin is your next language?
Why Kotlin is your next language? Why Kotlin is your next language?
Why Kotlin is your next language?
 
Introduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demoIntroduction to kotlin + spring boot demo
Introduction to kotlin + spring boot demo
 
golang_getting_started.pptx
golang_getting_started.pptxgolang_getting_started.pptx
golang_getting_started.pptx
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android Update
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
 
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguajeKotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
Kotlin+MicroProfile: Enseñando trucos de 20 años a un nuevo lenguaje
 
Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#Tuga IT 2018 Summer Edition - The Future of C#
Tuga IT 2018 Summer Edition - The Future of C#
 
Clojure class
Clojure classClojure class
Clojure class
 
PHP and MySQL
PHP and MySQLPHP and MySQL
PHP and MySQL
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
NetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf EditionNetPonto - The Future Of C# - NetConf Edition
NetPonto - The Future Of C# - NetConf Edition
 

More from Naresha K

The Groovy Way of Testing with Spock
The Groovy Way of Testing with SpockThe Groovy Way of Testing with Spock
The Groovy Way of Testing with Spock
Naresha K
 
Evolving with Java - How to Remain Effective
Evolving with Java - How to Remain EffectiveEvolving with Java - How to Remain Effective
Evolving with Java - How to Remain Effective
Naresha K
 
Take Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainersTake Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainers
Naresha K
 
Implementing Resilience with Micronaut
Implementing Resilience with MicronautImplementing Resilience with Micronaut
Implementing Resilience with Micronaut
Naresha K
 
Take Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainersTake Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainers
Naresha K
 
Favouring Composition - The Groovy Way
Favouring Composition - The Groovy WayFavouring Composition - The Groovy Way
Favouring Composition - The Groovy Way
Naresha K
 
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good PracticesEffective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Naresha K
 
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
Naresha K
 
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Naresha K
 
Eclipse Collections, Java Streams & Vavr - What's in them for Functional Pro...
Eclipse Collections, Java Streams & Vavr - What's in them for  Functional Pro...Eclipse Collections, Java Streams & Vavr - What's in them for  Functional Pro...
Eclipse Collections, Java Streams & Vavr - What's in them for Functional Pro...
Naresha K
 
Implementing Cloud-Native Architectural Patterns with Micronaut
Implementing Cloud-Native Architectural Patterns with MicronautImplementing Cloud-Native Architectural Patterns with Micronaut
Implementing Cloud-Native Architectural Patterns with Micronaut
Naresha K
 
Groovy - Why and Where?
Groovy  - Why and Where?Groovy  - Why and Where?
Groovy - Why and Where?
Naresha K
 
Leveraging Micronaut on AWS Lambda
Leveraging Micronaut on AWS LambdaLeveraging Micronaut on AWS Lambda
Leveraging Micronaut on AWS Lambda
Naresha K
 
Groovy Refactoring Patterns
Groovy Refactoring PatternsGroovy Refactoring Patterns
Groovy Refactoring Patterns
Naresha K
 
Implementing Cloud-native Architectural Patterns with Micronaut
Implementing Cloud-native Architectural Patterns with MicronautImplementing Cloud-native Architectural Patterns with Micronaut
Implementing Cloud-native Architectural Patterns with Micronaut
Naresha K
 
Effective Java with Groovy
Effective Java with GroovyEffective Java with Groovy
Effective Java with Groovy
Naresha K
 
Evolving with Java - How to remain Relevant and Effective
Evolving with Java - How to remain Relevant and EffectiveEvolving with Java - How to remain Relevant and Effective
Evolving with Java - How to remain Relevant and Effective
Naresha K
 
Effective Java with Groovy - How Language can Influence Good Practices
Effective Java with Groovy - How Language can Influence Good PracticesEffective Java with Groovy - How Language can Influence Good Practices
Effective Java with Groovy - How Language can Influence Good Practices
Naresha K
 
Beyond Lambdas & Streams - Functional Fluency in Java
Beyond Lambdas & Streams - Functional Fluency in JavaBeyond Lambdas & Streams - Functional Fluency in Java
Beyond Lambdas & Streams - Functional Fluency in Java
Naresha K
 
GORM - The polyglot data access toolkit
GORM - The polyglot data access toolkitGORM - The polyglot data access toolkit
GORM - The polyglot data access toolkit
Naresha K
 

More from Naresha K (20)

The Groovy Way of Testing with Spock
The Groovy Way of Testing with SpockThe Groovy Way of Testing with Spock
The Groovy Way of Testing with Spock
 
Evolving with Java - How to Remain Effective
Evolving with Java - How to Remain EffectiveEvolving with Java - How to Remain Effective
Evolving with Java - How to Remain Effective
 
Take Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainersTake Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainers
 
Implementing Resilience with Micronaut
Implementing Resilience with MicronautImplementing Resilience with Micronaut
Implementing Resilience with Micronaut
 
Take Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainersTake Control of your Integration Testing with TestContainers
Take Control of your Integration Testing with TestContainers
 
Favouring Composition - The Groovy Way
Favouring Composition - The Groovy WayFavouring Composition - The Groovy Way
Favouring Composition - The Groovy Way
 
Effective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good PracticesEffective Java with Groovy - How Language Influences Adoption of Good Practices
Effective Java with Groovy - How Language Influences Adoption of Good Practices
 
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
Effective Java with Groovy & Kotlin - How Languages Influence Adoption of Goo...
 
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
 
Eclipse Collections, Java Streams & Vavr - What's in them for Functional Pro...
Eclipse Collections, Java Streams & Vavr - What's in them for  Functional Pro...Eclipse Collections, Java Streams & Vavr - What's in them for  Functional Pro...
Eclipse Collections, Java Streams & Vavr - What's in them for Functional Pro...
 
Implementing Cloud-Native Architectural Patterns with Micronaut
Implementing Cloud-Native Architectural Patterns with MicronautImplementing Cloud-Native Architectural Patterns with Micronaut
Implementing Cloud-Native Architectural Patterns with Micronaut
 
Groovy - Why and Where?
Groovy  - Why and Where?Groovy  - Why and Where?
Groovy - Why and Where?
 
Leveraging Micronaut on AWS Lambda
Leveraging Micronaut on AWS LambdaLeveraging Micronaut on AWS Lambda
Leveraging Micronaut on AWS Lambda
 
Groovy Refactoring Patterns
Groovy Refactoring PatternsGroovy Refactoring Patterns
Groovy Refactoring Patterns
 
Implementing Cloud-native Architectural Patterns with Micronaut
Implementing Cloud-native Architectural Patterns with MicronautImplementing Cloud-native Architectural Patterns with Micronaut
Implementing Cloud-native Architectural Patterns with Micronaut
 
Effective Java with Groovy
Effective Java with GroovyEffective Java with Groovy
Effective Java with Groovy
 
Evolving with Java - How to remain Relevant and Effective
Evolving with Java - How to remain Relevant and EffectiveEvolving with Java - How to remain Relevant and Effective
Evolving with Java - How to remain Relevant and Effective
 
Effective Java with Groovy - How Language can Influence Good Practices
Effective Java with Groovy - How Language can Influence Good PracticesEffective Java with Groovy - How Language can Influence Good Practices
Effective Java with Groovy - How Language can Influence Good Practices
 
Beyond Lambdas & Streams - Functional Fluency in Java
Beyond Lambdas & Streams - Functional Fluency in JavaBeyond Lambdas & Streams - Functional Fluency in Java
Beyond Lambdas & Streams - Functional Fluency in Java
 
GORM - The polyglot data access toolkit
GORM - The polyglot data access toolkitGORM - The polyglot data access toolkit
GORM - The polyglot data access toolkit
 

Recently uploaded

SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
Hironori Washizaki
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Neo4j
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
mz5nrf0n
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
lorraineandreiamcidl
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Łukasz Chruściel
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
Boni García
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 

Recently uploaded (20)

SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
在线购买加拿大英属哥伦比亚大学毕业证本科学位证书原版一模一样
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️Need for Speed: Removing speed bumps from your Symfony projects ⚡️
Need for Speed: Removing speed bumps from your Symfony projects ⚡️
 
openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 

What's in Groovy for Functional Programming

  • 1. What’s in Groovy for Functional Programming Naresha K @naresha_k https://blog.nareshak.com/ APACHECON @HOME Spt, 29th – Oct. 1st 2020
  • 2. About me Developer, Architect & Tech Excellence Coach Founder & Organiser Bangalore Groovy User Group 2
  • 4. 4
  • 5. String greet(String message) { "Hello, ${message}" } String greet() { "Hello" } String greet(String message, String friend) { "Hello $friend, $message" } Regular Functions 5
  • 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. String greet() { "Hello" } def greet = { -> "Hello" } println greet() 7
  • 8. String greet(String message, String friend) { "Hello $friend, $message" } def greet = { message, friend -> "Hello $friend, $message" } println greet() 8
  • 9. def greet = { message -> "Hello, ${message}" } def message = "Hello" Integer age = 30 println greet("Good morning") Closures are first class 9
  • 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. 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. 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. 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. 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. 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. Closure knowsGroovy = createSkillFinder('Groovy') Closure knowsJava = createSkillFinder('Java') println findDevelopers(developers, knowsGroovy) println findDevelopers(developers, knowsJava) 16
  • 17. Closure knowsGroovy = createSkillFinder('Groovy') Closure knowsJava = createSkillFinder('Java') println findDevelopers(developers, knowsGroovy) println findDevelopers(developers, knowsJava) Strategy Pattern 17
  • 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. 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. 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. def add = { a, b -> a + b } println add(10, 20) 21
  • 22. def add = { a, b -> a + b } println add(10, 20) def increment = add.curry(1) println increment(10) Partial Application/ Currying 22
  • 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. 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. 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
  • 27. Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy) Closure javaDevSelector = findDevelopers.rcurry(knowsJava) println firstNamesOfDevs(developers, javaAndGroovyDevSelector) 27
  • 28. Closure groovyDevSelector = findDevelopers.rcurry(knowsGroovy) Closure javaDevSelector = findDevelopers.rcurry(knowsJava) Closure javaAndGroovyDevSelector = groovyDevSelector << javaDevSelector println firstNamesOfDevs(developers, javaAndGroovyDevSelector) Function Composition 28
  • 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. 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
  • 32. // Instead of developers.sort() // Use sortedData = developers.sort(false) 32
  • 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. def numbers = [1, 2, 3, 4, 5] println numbers.inject(0) { s, item -> s + item } Iteration/ fold left 35
  • 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. 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. 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. import groovy.transform.Memoized int timeConsumingOp(int number) { Thread.sleep(5000) number * number } println timeConsumingOp(10) println timeConsumingOp(10) println timeConsumingOp(10) 39
  • 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
  • 42. Effective Java With Groovy - How Language Influences Adoption of Good Practices APACHECON @HOME Spt, 29th – Oct. 1st 2020 42
  • 43. Thank You APACHECON @HOME Spt, 29th – Oct. 1st 2020 43