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 through 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). For example, this would allow values to be cached (saved in memory).

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

Although functional programming could be achieved in Java pre-Java-8,Footnote 1 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 FP languages.

FormalPara Note

Prominent functional programming languages such as Common Lisp, Scheme, Clojure, Racket, Erlang, OCaml, Haskell, and F# have been used in industrial and commercial applications by a wide variety of organizations. ClojureFootnote 2 is a Lisp-like language that runs on the JVM.

Functions and Closures

Functions as a first-class feature is the basis of functional programming. First-class feature means that a function can be used anywhere a value can be used.

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

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. A closure is simply a block of code wrapped in curly brackets with parameters defined 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, the following has the same meaning as the preceding closr:

1   def closr = {it + 1}

Java 8 introduced the lambda expression, which is something like a closure that implements a functional interface (an interface with a single abstract method). The main syntax of a lambda expression is as follows:

parameters -> body

The Java compiler uses the context of the expression to determine which functional interface is being used (and the types of the parameters). For example:

1   Function<Integer,Integer> func = x -> x + 1; 2   int three = func.apply(2); //3

Here, the functional interface is Function<T,R>, which has the apply method—T being the parameter type and R being the return type. The return value and parameter type are both Integers, thus Integer,Integer are the generic type parameters.

In Java 8, a functional interface is defined as an interface with exactly one abstract method. This even applies to interfaces that were created with previous versions of Java.

In Scala, everything is an expression and functions are a first-class feature. Here’s a function example in Scala:

1   var  f =  (x:  Int) =>  x + 1; 2   println(f(2)); //3

Although both Java and Scala are statically typed, Scala actually uses the right-hand side to infer the type of function being declared, whereas Java does the opposite in most cases. Java 11 introduced the local variable type var which allows syntax very close to Scala’s var.

In Java, Groovy, and Scala, the return keyword can be omitted, if there is one expression in the function/closure. However, in Groovy and Scala, the return keyword can also be omitted, if the returned value is the last expression.

Map, Filter, etc.

Once you have mastered functions, you quickly realize that you need a way to perform operations on collections (or sequences or streams) of data. Because these are common operations, sequence operations, such as map, filter, reduce, etc., were invented.

We’ll use JavaScript for the examples in this section, because it is easier to read, and the function names are fairly standard across programming languages. Create the following Person prototype and Array:

1 function Person(name, age) { this.name = name; this.age = age; } 2 var persons = [new Person("Bob", 18), 3     new Person("Billy", 21), new Person("sam", 12)]

The map function translates or changes input elements into something else (Figure 10-1). The following code collects the names of each person:

1   var names = persons.map(function(person) { return person.name })

Figure 10-1
figure 1

Map

filter gives you a subset of elements (what returns true from some predicate function—a function that returns a boolean given one argument [Figure 10-2]). For example, the following code collects only those persons with an age greater than or equal to 18:

1   var adults = persons.filter(function(person) { return person.age >= 18 })

Figure 10-2
figure 2

Filter

reduce performs a reduction on the elements (Figure 10-3). For example, the following code collects the total age of all persons:

1   var totalAge = persons.reduce(function(total, p) { return total+p.age },0)

Figure 10-3
figure 3

Reduce

limit gives you only the first N elements (Figure 10-4). In JavaScript you can achieve this using the Array.slice(start, end) function. For example, the following code gets the first two persons:

1   var firstTwo = persons.slice(0, 2)

Figure 10-4
figure 4

Limit

concat combines two different collections of elements (Figure 10-5). This can be done in JavaScript like the following example:

1 var morePersons = [new Person("Mary", 55), new Person("Sue", 22)] 2 var all = persons.concat(morePersons);

Figure 10-5
figure 5

Concat

Immutability

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

In purely functional languages, the idea is that each function has no effect outside itself—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, as you learned earlier, 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 the preceding, map would remain unchanged.

Each language has a keyword for defining immutable variables (values):

  • Scala uses the val keyword to denote immutable values, as opposed to var, which is used for mutable variables.

  • Java has the final keyword for declaring variables immutable (this only stops the value from being modified, if it’s a reference to another object, that object’s variables could still be modified).

  • In addition to the final keyword , Groovy includes the @Immutable annotationFootnote 3 for declaring a whole class immutable.

  • JavaScript uses the const keyword.Footnote 4

For example (in Groovy):

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 8   c.name = "Firenze" //error

This works for simple references and primitives, such as numbers and strings, but for things such as lists and maps, it’s more complicated. For these cases, open source immutable libraries have been developed for the languages in which it’s not included, such as the following:

Java

In Java 8, the Stream<T> interface was introduced. A stream is like an improved iterator that supports chaining methods to perform complex operations.

To use a stream, you must first create one in one of the following ways:

  • Collection's stream() method or parallelStream() method: These create a stream backed by the collection. Using the parallelStream() method causes the stream operations to be run in parallel.

  • Arrays.stream() method: Used for converting arrays to streams.

  • Stream.generate(Supplier<T> s): Returns an infinite sequential stream in which each element is generated by the given supplier.

  • Stream.iterate(T seed, UnaryOperator<T> f): Returns an infinite sequential ordered stream produced by iterative application of a function to an initial element seed, producing a stream consisting of seed, f(seed), f(f(seed)), etc.

Once you have a stream, you can then use filter, map, and reduce operations to concisely perform calculations on the data. For example, the following code finds the longest name from a list of dragons:

1   String longestName = dragons.stream() 2       .filter(d -> d.name != null) 3       .map(d -> d.name) 4       .reduce((n1, n2) -> n1.length() > n2.length() ? n1 : n2) 5       .get();

Groovy

In Groovy , findAll and other methods are available on every object but are especially useful for lists and sets. The following method names are used in Groovy:

  • findAll: Much like filter, it finds all elements that match a closure.

  • collect: Much like map, this is an iterator that builds a collection.

  • inject: Much like reduce, it loops through the values and returns a single value.

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

  • eachWithIndex: Iterates through with two parameters: a value and an index (the index of the value, starting at zero and going up).

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

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

  • any: True if any element returns true for the closure.

  • every: True if all elements return true for the closure.

For example, the following assumes dragons is a list of Dragon objects which have a name property:

1   String longestName = dragons 2      .findAll { it.name != null } 3      .collect { it.name } 4      .inject("") { n1, n2 -> n1.length() > n2.length() ? n1 : n2 }

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

Scala

Scala has many such methods on its built-in collections, including the following:

  • map: Converts values from one value to another

  • flatMap: Converts values to collections of values and then concatenates the results together (similar to the flatten() method in Groovy)

  • filter: Limits the returned values, based on some Boolean expression

  • find: Returns the first value matching the given predicate

  • forAll: True only if all elements match the given predicate

  • exists: True if at least one element matches the given predicate

  • foldLeft: Reduces the values to one value using the given closure, starting at the last element and going left

  • foldRight: Same as foldLeft, but starting from the first value and going up (similar to reduce in Java)

For example, you can use map to perform an operation on a list of values, as follows:

1   val list = List(1, 2, 3) 2   list.map(_  * 2) // List(2, 4, 6)

Much like it in Groovy, in Scala, you can use the underscore to reference a single argument.

Assuming dragons is a List of dragon objects, you can do the following in Scala to determine the longest name:

1   var longestName = dragons.filter(_ != null).map(_.name).foldRight("")( 2       (n1:String,n2:String) => if (n1.length() > n2.length()) n1 else n2)

Summary

In this chapter, you should have learned about

  • Functions as a first-class feature

  • Map, filter, reduce

  • Immutability and how it relates to FP

  • Various features supporting FPs in Java, Groovy, Scala, and JavaScript