Higher-order functions with Groovy
I'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:
1.defx = {printlnit }And here's how you call it (two options):
1.x('Hello, world!')2.x.call('Hello, world')Closures can take arguments, including other closures:
1.defisList = { i -> iinstanceofList }2.if(isList([])) {3.println"This is a List"4.}Closure arguments can be typed:
1.defprefix = {2.String s ->3.while(s.length() <17) {4.s ="0$s"5.}6.s// return keyword is not required7.}8.defid = prefix"1234"// parentheses are not requiredClosures can be passed as arguments, for example to the each() method on
java.util.Map:1.System.properties.each{printlnit }And a closure can call itself recursively:
1.// Thanks to Sergey Bondarenko for this one-liner2.deffac = {inti -> i ==1?1: i * call(i -1) }3.printlnfac(10)// parentheses are required for fac since I call println withoutClosures can access the variables in the scope where they are declared:
1.defpi =22/32.defcalcSurface = { radius -> pi * (radius * radius) }3.defsurface = calcSurface10Currying is closely related to closures. With curring you can construct programs by appending argument values to closures:
01.defappendForLength = {02.intlength, String charachter, String toBeAppended ->03.04.while(toBeAppended.length() < length) {05.toBeAppended ="${character}${toBeAppended}"06.}07.returntoBeAppended08.}09.defmyKindOfId = appendForLength.curry17,"0"10.assert"00000000000012345"== myKindOfId("12345")The call to the
curry()method on line 9 passes two arguments to theappendForLengthclosure and returns a new closure. This new closure takes one argument which is actually the third argument of theappendForLengthclosure.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:1.defgetProperty = System.&getPropertyThe value that is returned is a closure:
1.defgetProperty = System.&getProperty2.getProperty("java.version")And since it's a closure you can curry it:
1.defgetProperty = System.&getProperty2.defjavaVersion = getProperty.curry("java.version")3.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!0Average: 3.8 (6 votes)
Nice three-part series on higher-order function (read: closures) from Steven Devijver over on DZone. Gives a nice explanation of currying (including an interesting example of currying Java methods), and one of the clearest explanations of closure delegates I've read. Definitely worth a read!

+
=