Higher-order Functions With Groovy, Part 1
An introduction to higher-order Groovy functions.
Join the DZone community and get the full member experience.
Join For FreeI'll admit, higher-order functions sounds like link bait for over-achievers. Trust me, I didn't invent the term :-) A Higher-order function is a concept from mathematics where a function accepts other functions as its arguments, and can return functions as results.
Higher-order functions are related to functional programming but higher-order functions != functional programming. In computer science higher-order functions consists of two things: closures and currying. Groovy supports both :-)
So what's a closure then? Closures are not unique to Groovy. Ruby, Lisp, JavaScript and D have closures as do many other languages.
A closure in Groovy is three things:
- a block of code that can access variables in the scope where it is declared.
- a function that can take arguments and always returns a result (may be
null
) - an object that has properties and methods with and without side-effects
Calling a closure if thread-safe if the implementation is thread-safe. Here's an example of a closure:
def x = { println it }
And here's how you call it (two options):
x('Hello, world!')
x.call('Hello, world')
Closures can take arguments, including other closures:
def isList = { i -> i instanceof List }
if (isList([])) {
println "This is a List"
}
Closure arguments can be typed:
def prefix = {
String s ->
while (s.length() < 17) {
s = "0$s"
}
s // return keyword is not required
}
def id = prefix "1234" // parentheses are not required
Closures can be passed as arguments, for example to the each() method on java.util.Map
:
System.properties.each { println it }
And a closure can call itself recursively:
// Thanks to Sergey Bondarenko for this one-liner
def fac = { int i -> i == 1 ? 1 : i * call(i - 1) }
println fac(10) // parentheses are required for fac since I call println without
Closures can access the variables in the scope where they are declared:
def pi = 22 / 3
def calcSurface = { radius -> pi * (radius * radius) }
def surface = calcSurface 10
Currying is closely related to closures. With curring you can construct programs by appending argument values to closures:
def appendForLength = {
int length, String charachter, String toBeAppended ->
while (toBeAppended.length() < length) {
toBeAppended = "${character}${toBeAppended}"
}
return toBeAppended
}
def myKindOfId = appendForLength.curry 17, "0"
assert "00000000000012345" == myKindOfId("12345")
The call to the curry()
method on line 9 passes two arguments to the appendForLength
closure and returns a new closure. This new closure takes one argument which is actually the third argument of the appendForLength
closure.
And with currying you can go beyond Groovy closures, you can also curry any Java method. First you need to know how to turn a method into a closure. You add the ampersand (&
) character in front of the method name:
def getProperty = System.&getProperty
The value that is returned is a closure:
def getProperty = System.&getProperty
getProperty("java.version")
And since it's a closure you can curry it:
def getProperty = System.&getProperty
def javaVersion = getProperty.curry("java.version")
assert "1.5.0_04" == javaVersion()
Higher-order functions simplify programs. It's closely related to functional programming. You've learned how to convert regular Java methods to higher-order functions. You can also do the inverse: convert higher-order functions to Java interface methods. This and other techniques will be the subject of the second installment.
Update:part two has been posted too.Happy coding!
Opinions expressed by DZone contributors are their own.
Comments