© Adam L. Davis 2016

Adam L. Davis, Learning Groovy, 10.1007/978-1-4842-2117-4_9

9. Functional Programming

Adam L. Davis

(1)New York, USA

Functional Programming (FP) is a programming style that focuses on functions and minimizes changes of state (using immutable data structures). It is closer to expressing solutions mathematically rather than step-by-step instructions.

In FP, functions should be “side-effect free ” (nothing outside the function is changed) and referentially transparent(a function returns the same value every time when given the same arguments).

FP can be seen as an alternative to the more common imperative programming, which is closer to telling the computer the steps to follow.

Although functional-programming can be achieved in Java pre-Java-81, Java 8 enabled language-level FP-support with Lambda expressions and functional interfaces.

Java 8, JavaScript, Groovy, and Scala all support functional-style programming, although they are not strictly FP languages.

Functions and Closures

As you might know, “functions as a first-class feature” is the basis of functional programming. First-class featuremeans that a function can be used anywhere a value could be used.

For example, in JavaScript, you can assign a function to a variable and call it:

1   var func = function(x) { return x + 1; }
2   var three = func(2); //3

Although Groovy doesn't have first-class functions, it has something very similar: closures. As you have learned, a closure is simply a block of code wrapped in curly brackets with parameters defined to the left of the -> (arrow). For example :

1   def  closr = {x -> x + 1}
2   println( closr(2) ); //3

If a closure has one argument, it can be referenced as it in Groovy. For example:

1   def  closr = {it + 1}
A426440_1_En_9_Figa_HTML.jpg Tip

In Groovy, the return keyword can be omitted if the returned value is the last expression.

Using Closures

When a closure is the last or only parameter to a method it can go outside of the parentheses. For example, the following defines a method that takes a list and a closure for filtering items:

1   def  find(list, tester) {
2       for (item in list)
3           if (tester(item)) return item
4   }

This method returns the first item in the list for which the closure returns true. Here's an example of calling the method with a simple closure:

1   find([1,2]) { it > 1 }

Map/Filter /And So On

Once you have mastered functions, you quickly realize you need a way to perform operations on collections (or sequences or streams) of data.

Since these are common operations, people invented sequence operationssuch as map, filter, reduce, etc.

For this section , we will be using a list of Person objects for all the operations (see Figure 9-1 through 9-5):

A426440_1_En_9_Fig1_HTML.gif
Figure 9-1. Map (collect): Translates or changes input elements into something else
A426440_1_En_9_Fig2_HTML.gif
Figure 9-2. Filter (findAll): Gives you a sub-set of elements (what returns true from some predicate function)
A426440_1_En_9_Fig3_HTML.gif
Figure 9-3. Reduce (inject): Performs a reduction (returning one result, such as a sum) on the elements
A426440_1_En_9_Fig4_HTML.gif
Figure 9-4. Limit ([0..n-1]): Gives you only the first N elements
A426440_1_En_9_Fig5_HTML.gif
Figure 9-5. Concat (+): Combines two different collections of elements
class Person { String name; int age }
def persons = [new Person(name:'Bob',age:20),
        new Person(name:'Tom',age:15)]
1   def names = persons.collect { person -> person.name }
1   def adults = persons.findAll { person -> return person.age >= 18 }
1   def totalAge = persons.inject(0) {(total, p) -> return total+p.age }

For this, we use the inject method, which loops through the values and returns a single value (equivalent of foldRight in Scala). The startValue (0 in this case) is the initial value given to total. For each element of the list, we add the person’s age.

1   def  firstTwo = persons[0..1]
1   def a = [1,2,3]
2   def  b = [4,5]
3   a+b
4   // Result: [1, 2, 3, 4, 5]

Immutability

Immutability and FP go together like peanut butter and jelly. Although it's not necessary, they go together nicely.

In purely functional languages the idea is that each function has no effect outside itself—that is, no side effects. This means that every time you call a function it returns the same value given the same inputs.

To accommodate this behavior, there are immutable data structures. An immutable data structure cannot be directly changed, but returns a new data structure with every operation.

For example, Scala's default Map is immutable:

1   val map =  Map("Smaug" -> "deadly")
2   val  map2 =  map + ("Norbert" -> "cute")
3   println(map2) // Map(Smaug -> deadly, Norbert -> cute)

So in this code, map would remain unchanged.

Each language has a keyword for defining immutable variables (values). Java has the final keyword for declaring immutable variables , which Groovy also respects.

1   public class Centaur {
2       final String name
3       public  Centaur(name) {this.name=name}
4   }
5   Centaur c = new  Centaur("Bane");
6   println(c.name) // Bane
7   c.name = "Firenze" //error

In addition to the final keyword, Groovy includes the @Immutable annotation 2 for declaring a whole class immutable. It also adds default constructors with parameter-order, hashCode, equals, and toString methods. For example (in Groovy):

1   import groovy.transform.Immutable          
2   @Immutable
3   public class Dragon  {
4       String name
5       int scales
6   }
7   Dragon smaug = new  Dragon('Smaug', 499)
8   println smaug
9   // Output: Dragon(Smaug, 499)

This works for simple references and primitives such as numbers and Strings, but for things like lists and maps, it's more complicated. For these cases, open source immutable libraries have been developed—for example Guava3 for Java and Groovy.

Groovy Fluent GDK

In Groovy, findAll and other methods are available on every object, but are especially useful for lists, sets, and ranges. In addition to findAll, collect, and inject, the following method names are used in Groovy:

  • each—Iterates through the values using the given closure.

  • eachWithIndex—Iterates through with two parameters: a value and an index.

  • find—Finds the first element that matches a closure.

  • findIndexOf—Finds the first element that matches a closure and returns its index.

For example, collect makes it very simple to perform an operation on a list of values:

1   def  list = ['foo','bar']
2   def newList = []
3   list.collect( newList ) { it.substring(1) }
4   println newList //  [oo, ar]

For another example, assuming dragons is a list of dragon objects:

1   def dragons = [new Dragon('Smaug', 499), new Dragon('Norbert', 488)]
2   String longestName = dragons.
3       findAll { it.name != null }.
4       collect { it.name }.
5       inject("") { n1, n2 -> n1.length() > n2.length() ? n1 : n2 }

The result should be Norbert. This code finds all non-null names, collects the names, and then reduces the list of names to the longest one.

A426440_1_En_9_Figa_HTML.jpg Tip

Remember that it in Groovy can be used to reference the single argument of a closure.

A426440_1_En_9_Figb_HTML.jpg Exercise

Using the previous code as a starting point, find the dragon with the most scales.

Groovy Curry

The curry method allows you to predefine values for parameters of a closure. It takes any number of arguments and replaces parameters from left to right as you might expect. For example, given a concat closure, you could create a burn closure and an inate closure easily as follows:

1   def  concat = { x, y -> return  x + y }
2   // closure
3   def  burn = concat.curry("burn")
4   def inate = concat.curry("inate")

Since you're only providing the first parameter, these closures are prepending their given text: burn prepends "burn" and inate prepends "inate". For example:

1   burn(" wood") // == burn wood          

You could then use another closure called composition to apply the two functions to one input.

1   def composition = { f, g, x -> return f(g(x)) }
2   def burninate = composition.curry(burn, inate)
3   def  trogdor = burninate(' all the people')
4   println "Trogdor: ${trogdor}"
5   // Trogdor: burninate all the people

Functional composition is an important idea in functional programming. It allows you compose functions together to create complex algorithms out of simple building blocks.

Method Handles

Method handles allow you to refer to actual methods much like they are closures. This is useful when you want to use existing methods instead of closures, or you just want an alternative syntax for closures. For example, given a method:

1   def breathFire(name) { println "Burninating $name!" }

You could later on do the following (within the same class that defined breathFire):

1   ['the country side', 'all the people'].each(this.&breathFire)

This would pass the breathFire method to the each method as a closure, causing the following to be printed:

1   Burninating the country side
2   Burninating all the people
A426440_1_En_9_Figb_HTML.jpg Exercise

Create a method with multiple parameters and attempt to call it using a method handle. Does it work as expected?

Tail Recursion

In Groovy 1.8, the trampoline method was introduced for a closure to use the tail recursion optimization . This allows closure invocations to be invoked sequentially instead of stacked to avoid a StackOverFlowError and improve performance.

Starting in Groovy 2.3, you can use trampoline for recursive methods but even better is the @TailRecursive AST transformation. Simply annotate a tail-recursive method with @TailRecursive and Groovy does the rest. For example:

1   import   groovy.transform.*          
2   @TailRecursive
3   long  totalPopulation(list, total = 0) {
4     if (list.size() == 0)
5       total
6     else
7       totalPopulation(list.tail(), total + list.first().population)
8   }
A426440_1_En_9_Figc_HTML.jpg Info

On a Groovy list, the tail method returns the list without the first element and first returns just the first element.

This would take a list of objects with a population property and get the sum of them. For example, let's make a City class, use a range to create a bunch of them, and then use our totalPopulation method:

1   @Canonical class City {int population}
2   def cities = (10..1000).collect{new City(it)}
3   totalPopulation(cities)
4   // 500455

This demonstrates how tail recursion can be used in a functional style as an alternative to iteration.

A426440_1_En_9_Figc_HTML.jpg Info

Tail recursion goes well with immutability because no direct modification of variables is necessary in tail recursion.

Summary

In this chapter, you learned about:

  • Functions as a first-class feature as closures.

  • Map/filter/reduce as collect/findAll/inject.

  • Immutability and how it relates to FP.

  • Various features that support FP in Groovy.

  • Method handles in Groovy.

  • Tail recursion optimization.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.134.154