Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Groovy's functions (like Java's) can be used to define functions which contain no imperative steps, e.g. a factorial function may look like:

Code Block

def fac(n) { n == 0 ? 1 : n * fac(n - 1) }
assert 24 == fac(4)

...

You can also define the above factorial function using a Closure:

Code Block

def fac2 = { n -> n == 0 ? 1 : n * call(n - 1) }
assert 24 == fac2(4)

...

We can also convert between functions and Closures, e.g.

Code Block

def fac3 = this.&fac
assert 24 == fac3(4)

...

We can of course start to mix and match functional and imperative coding styles as in this quicksort-like example:

Code Block

def sort(list) {
    if (list.isEmpty()) return list
    anItem = list[0]
    def smallerItems = list.findAll{it < anItem}
    def equalItems = list.findAll{it == anItem}
    def largerItems = list.findAll{it > anItem}
    sort(smallerItems) + equalItems + sort(largerItems)
}

assert [1, 3, 4, 5] == sort([1, 4, 5, 3])
assert [1, 1, 3, 4, 4, 5, 8] == sort([4, 1, 4, 1, 8, 5, 3])
assert ['a', 'b', 'c'] == sort(['c', 'b', 'a'])

...

You can fix the values for one or more arguments to a closure instance using the curry() method as follows:

Code Block

def joinTwoWordsWithSymbol = { symbol, first, second -> first + symbol + second }
assert joinTwoWordsWithSymbol('#', 'Hello', 'World') == 'Hello#World'

def concatWords = joinTwoWordsWithSymbol.curry(' ')
assert concatWords('Hello', 'World') == 'Hello World'

def prependHello = concatWords.curry('Hello')
// def prependHello = joinTwoWordsWithSymbol.curry(' ', 'Hello')
assert prependHello('World') == 'Hello World'

...

See reference 2 below for all the details, but to give you a flavour, first you must define some lazy list handling functions, then you can define and use infinite streams. Here is an example:

Code Block

// general purpose lazy list class
class LazyList {
    def car
    private Closure cdr
    LazyList(def car, Closure cdr) { this.car=car; this.cdr=cdr }
    def LazyList getCdr() { cdr ? cdr.call() : null }
    def List take(n) {
        def r = []; def l = this
        n.times { r.add(l.car); l = l.cdr }
        r
    }
    def LazyList filter(Closure pred) {
        if (pred(car)) return pred.owner.cons(car, { getCdr().filter(pred) })
        else return getCdr().filter(pred)
    }
}

// general purpose lazy list function
def cons(val, Closure c) { new LazyList(val, c) }

// now define and use infinite streams
def integers(n) { cons(n, { integers(n+1) }) }
def naturalnumbers = integers(1)
assert '1 2 3 4 5 6 7 8 9 10' == naturalnumbers.take(10).join(' ')
def evennumbers = naturalnumbers.filter{ it % 2 == 0 }
assert '2 4 6 8 10 12 14 16 18 20' == evennumbers.take(10).join(' ')

If you are not familiar with traditional functional programming terms like cons, car and cdr, you may find the refactored version below (creds to Alexander Kriegisch) somewhat more readable:

Code Block

class LazyList {
    def head
    private Closure tail
    LazyList(def head, Closure tail) { this.head=head; this.tail=tail }
    def LazyList getTail() { tail ? tail.call() : null }
    def List getFirst(n) {
        def result = []; def current = this
        n.times { result.add(current.head); current = current.tail }
        result
    }
    def LazyList filter(Closure matchExpr) {
        if (matchExpr(head))
            return matchExpr.owner.prepend(head, { getTail().filter(matchExpr) })
        else
            return getTail().filter(matchExpr)
    }
}
 
// general purpose lazy list function
def prepend(val, Closure c) { new LazyList(val, c) }
 
// now define and use infinite streams
def integers(n) { prepend(n, { integers(n+1) }) }
def naturalnumbers = integers(1)
assert '1 2 3 4 5 6 7 8 9 10' == naturalnumbers.getFirst(10).join(' ')
def evennumbers = naturalnumbers.filter{ it % 2 == 0 }
assert '2 4 6 8 10 12 14 16 18 20' == evennumbers.getFirst(10).join(' ')

...

You can also consider using Java libraries which support functional programming, e.g. using Functional Java for an example similar to the evens example above:

Code Block

import fj.data.Stream

// some metaprogramming to make fj mesh well with Groovy
Stream.metaClass.filter = { Closure c -> delegate.filter(c as fj.F) }
Stream.metaClass.getAt = { n -> delegate.index(n) }
Stream.metaClass.getAt = { Range r -> r.collect{ delegate.index(it) } }
Stream.metaClass.asList = { delegate.toCollection().asList() }

def evens = Stream.range(0).filter{ it % 2 == 0 }
assert evens.take(5).asList() == [0, 2, 4, 6, 8]
assert (8..12).collect{ evens[it] } == [16, 18, 20, 22, 24]
assert evens[3..6] == [6, 8, 10, 12]

Or for something a little more adventurous, we can consider calculating primes:

Code Block

import fj.data.Stream
import static fj.data.Stream.cons

// some metaprogramming to make Closures mesh well with fj functions and products
Stream.metaClass.filterTail = { Closure c -> delegate.tail()._1().filter(c as fj.F) }
Stream.metaClass.static.cons = { h, Closure c -> delegate.cons(h, ['_1':c] as fj.P1) }

def sieve(nums) {
  cons nums.head(), { sieve nums.filterTail{ n -> n % nums.head() != 0 } }
}

println sieve(Stream.range(2)).index(100) // => 547

...

Another example:

Code Block

@Grab('org.functionaljava:functionaljava:3.0')
import static fj.data.Array.array
import static fj.data.List.list
import static fj.Show.*
import fj.F

arrayShow(intShow).println(array(1, 2, 3).map({ it + 42 } as F)) // => {43,44,45}
assert array(1, 2, 3).map({ it + 42 } as F).toCollection().toList() == [43, 44, 45]
assert [1, 2, 3].collect{ it + 42 } == [43, 44, 45] // native Groovy for comparison

listShow(intShow).println(list(1, 2, 3).bind({ it % 2 ? list(it * 2) : list() } as F)) // => <2,6>
assert list(1, 2, 3).bind({ it % 2 ? list(it * 2) : list() } as F).toCollection().toList() == [2, 6]
assert [1, 2, 3].collect{ it % 2 ? [it * 2] : [] }.sum() == [2, 6] // native Groovy for comparison

Or with a little bit of metaprogramming like so:

Code Block

import static fj.data.Java.ArrayList_List as asList
fj.data.List.metaClass.toList = { delegate.toCollection().toList() }
List.metaClass.bind = { c -> asList().f(delegate).bind({ asList().f(c(it)) } as F) }
List.metaClass.map = { c -> asList().f(delegate).map(c as F) }

The above examples can be re-written like this:

Code Block

assert [1, 2, 3].bind{ it % 2 ? [it * 2] : [] }.toList() == [2, 6]
assert [1, 2, 3].map{ it + 42 }.toList() == [43, 44, 45]

More Information

See also:

  1. Functional programming in Groovy (presentation)
  2. Functional Groovy (presentation)
  3. Functional thinking: Functional features in Groovy
  4. Practically Groovy: Functional programming with curried closures
  5. Infinite Streams in Groovy
  6. Functional Programming Languages
  7. Why Functional Programming Matters
  8. Functional programming in the Java language
  9. Post on functional programming in Java - maybe a tad verbose
  10. FunctionalJ - A library for Functional Programming in Java
  11. Weak versus strong languages, who wins the fight?
  12. Programming Languages:Application and Interpretation
  13. Beyond Groovy 1.0: Groovy goes Lisp
  14. Concurrency with Groovy