Lets get started with an example
Lets do it in imperative way first.
Lets make it more idiomatic groovy
Groovy provdes a syntactic sugure - if the last argument to a method is a closure, then it can be specified outside the parenteses.
Well thats ok, but what if I want find out all the ‘Clojure’ developers?
Lets generalize
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'])
Q - Find out all geeks who know Groovy. Lets do it in imperative way first.
def groovyGeeks = []
for(geek in geeks){
if(geek.languages.contains('Groovy')){
groovyGeeks << geek
}
}
println groovyGeeks
And we get the following result[Geek(name:Raj, age:24, languages:[Java, Groovy]), Geek(name:Kumar, age:28, languages:[Groovy, Scala])]
Does this look great? - Not really. Lets make it more idiomatic groovy
def groovyGeeks = geeks.findAll({it.languages.contains('Groovy')})
println groovyGeeks
Here I have used findAll
method, which accepts a closure - a predicate, which decides if an item in the collection to be part of the output. it
is the default parameter.Groovy provdes a syntactic sugure - if the last argument to a method is a closure, then it can be specified outside the parenteses.
def groovyGeeks = geeks.findAll{
it.languages.contains('Groovy')
}
println groovyGeeks
Lets refactor the code slightly, and extract the closure into a variable.def knowsGroovy = { geek -> geek.languages.contains('Groovy')}
def groovyGeeks = geeks.findAll{
knowsGroovy(it)
}
println groovyGeeks
If you have programmed in any functional language, you could recognize findAll
as a higher order function.Well thats ok, but what if I want find out all the ‘Clojure’ developers?
Lets generalize
knowsGroovy
to work for any language.def knowsLanguage = {geek, language ->
geek.languages.contains(language)}
def findAllExperts = { devs, language ->
devs.findAll { knowsLanguage(it, language) }
}
println findAllExperts(geeks, 'Groovy')
Now suppose you want to invoke findAllExperts
on several collections with the second argument fixed as ‘Groovy’, you could create a curried closure as followsdef findAllGroovyExperts = findAllExperts.rcurry('Groovy')
println findAllGroovyExperts(geeks)