Pattern 2Replacing State-Carrying Functional Interface

Intent

To encapsulate a bit of state along with program logic so it can be passed around, stored in data structures, and generally treated like any other first-class construct

Overview

In Pattern 1, Replacing Functional Interface, we saw how to replace Functional Interface with higher-order functions, but the instances we looked at didn’t carry around any program state. In this pattern, we’ll take a look at how we can replace Functional Interface implementations that need state using a powerful construct called a closure.

Also Known As

Function Object
Functoid
Functor

Functional Replacement

Functions in the functional world are part of a powerful construct called a closure. A closure wraps up a function along with the state available to it when it was created. This means that a function can reference any variable that was in scope when the function was created at the time it’s called. The programmer doesn’t have to do anything special to create a closure; the compiler and runtime take care of it, and the closure simply captures all the state that it needs automatically.

In classic Java, we’d carry state around by creating fields on the class and by providing setters for them or setting them through a constructor. In the functional world, we can take advantage of closures to handle this without any extra machinery. Closures are a bit magical, so it’s worth examining them in more detail before we move on.

A closure is composed of a function and the state that was available to that function when it was created. Let’s see what this might look like pictorially, as shown in the figure.

A closure is a structure that consists of a function and its context at the time it was defined.

images/ChapOneClosure.png

Figure 2. Structure of a Closure.

Here we can see that the closure has a function in it and a scope chain that lets it look up any variables that it needs to do its job. Translated into Clojure, it looks like this:

ClojureExamples/src/mbfpp/rso/closure_example.clj
 
(​ns​ mbfpp.rso.closure-example)
 
 
; Scope 1 - Top Level
 
(​def​ foo ​"first foo"​)
 
(​def​ bar ​"first bar"​)
 
(​def​ baz ​"first baz"​)
 
 
(​defn​ make-printer [foo bar] ​; Scope 2 - Function Arguments
 
(​fn​ []
 
(​let​ [foo ​"third foo"​] ​; Scope 3 - Let Statement
 
(​println​ foo)
 
(​println​ bar)
 
(​println​ baz))))

If we use this code to make a printer function and run it, it prints the foo, bar, and baz from the deepest scope that they’re defined in, just as any experienced developer would expect:

 
=> (def a-printer (make-printer "second foo" "second bar"))
 
#'closure-example/a-printer
 
=> (a-printer)
 
third foo
 
second bar
 
first baz
 
nil

This may not seem surprising, but what if we took a-printer and passed it around our program, or stored it in a vector to retrieve and use later? It would still print the same values for foo, bar, and baz, which implies that those values stick around somewhere.

Behind the scenes, Clojure and Scala need to do an awful lot of magic to make that work. However, actually using a closure is as simple as declaring a function. I like to keep the previous figure in mind when working with closures because it’s a good mental model of how they work.

Sample Code: Closure

To demonstrate closures, we’ll take one last look at comparisons, with a bit of a twist. This time, we’ll see how to create a comparison that’s composed of a list of other comparisons, which means that we need someplace to store this list of comparisons.

In Java, we’ll just pass them in as arguments to a constructor in our custom Comparator implementation, and we’ll store them in a field. In Scala and Clojure, we can just use a closure. Let’s jump into the Java example first.

Classic Java

In Java, we create a custom implementation of Comparator called ComposedComparator, with a single constructor that uses varargs to get an array of Comparators and stores them in a field.

When the compare method on a ComposedComparator is called, it runs through all the comparators in its array and returns the first nonzero result. If all the results are zero, then it returns zero. An outline of this solution looks like so:

 
public​ ​class​ ComposedComparator<T> ​implements​ ​Comparator​<T> {
 
private​ ​Comparator​<T>​[]​ comparators;
 
public​ ComposedComparator(​Comparator​<T>... comparators) {
 
this.comparators = comparators;
 
}
 
@Override
 
public​ ​int​ compare(T o1, T o2) {
 
//Iterate through comparators and call each in turn.
 
}
 
 
}

In the functional world, we can use closures instead of having to create new classes. Let’s dig into how to do this in Scala.

In Scala

In Scala, we’ll take advantage of closures to avoid explicitly keeping track of our list of comparisons in our composed comparison. Our Scala solution is centered around a higher-order function, makeComposedComparison, which uses varargs to take in an array of comparison functions and returns a function that executes them in order.

One other difference between the Java and Scala solutions is in how we return the final result. In Java, we iterated through the list of Comparators, and as soon as we saw a nonzero comparison, we returned it.

We use map to run our comparisons over our input. Then we search for the first one that’s nonzero. If we don’t find a nonzero value, all our comparisons were the same and we return zero. Here’s the code for makeComposedComparison:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/fi/personexpanded/ClosureExample.scala
 
def​ makeComposedComparison(comparisons: (Person, Person) => ​Int​*) =
 
(p1: Person, p2: Person) =>
 
comparisons.map(cmp => cmp(p1, p2)).find(_ != 0).getOrElse(0)

Now we can take two comparison functions and compose them together. In the code below, we define firstNameComparison and lastNameComparison, and then we compose them together into firstAndLastNameComparison:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/fi/personexpanded/ClosureExample.scala
 
def​ firstNameComparison(p1: Person, p2: Person) =
 
p1.firstName.compareTo(p2.firstName)
 
 
def​ lastNameComparison(p1: Person, p2: Person) =
 
p1.lastName.compareTo(p2.lastName)
 
 
val​ firstAndLastNameComparison = makeComposedComparison(
 
firstNameComparison, lastNameComparison
 
)

Let’s take a look at our composed comparison function in action by defining a couple of people and comparing them:

ScalaExamples/src/main/scala/com/mblinn/mbfpp/oo/fi/personexpanded/ClosureExample.scala
 
val​ p1 = Person(​"John"​, ​""​, ​"Adams"​)
 
val​ p2 = Person(​"John"​, ​"Quincy"​, ​"Adams"​)
 
scala>​ firstAndLastNameComparison(p1, p2)
 
res0: Int = 0

One optimization we could make is to create a short-circuiting version of the composed comparison that stops running comparisons as soon as it comes across the first nonzero result. To do so, we could use a recursive function such as the ones we discuss in Pattern 12, Tail Recursion, rather than the for comprehension we use here.

In Clojure

We’ll wrap up the code samples for this pattern with a look at how we’d create composed comparisons in Clojure. We’ll rely on a make-composed comparison, but it’ll work a bit differently than the Scala version.

In Scala, we could use the find method to find the first nonzero result; in Clojure we can use the some function. This is very different than Scala’s Some type!

In Clojure, the some function takes a predicate and a sequence, and it returns the first value for which the predicate is true. Here we use Clojure’s some and for to run all of the comparisons and select the correct final value:

ClojureExamples/src/mbfpp/rso/closure_comparison.clj
 
(​defn​ make-composed-comparison [& comparisons]
 
(​fn​ [p1 p2]
 
(​let​ [results (​for​ [comparison comparisons] (comparison p1 p2))
 
first-non-zero-result
 
(​some​ (​fn​ [result] (​if​ (​not​ (​=​ 0 result)) result nil)) results)]
 
(​if​ (​nil?​ first-non-zero-result)
 
0
 
first-non-zero-result))))

Now we can create our first-name-comparison and last-name-comparison and compose them together:

ClojureExamples/src/mbfpp/rso/closure_comparison.clj
 
(​defn​ first-name-comparison [p1, p2]
 
(​compare​ (:first-name p1) (:first-name p2)))
 
 
(​defn​ last-name-comparison [p1 p2]
 
(​compare​ (:last-name p1) (:last-name p2)))
 
 
(​def​ first-and-last-name-comparison
 
(make-composed-comparison
 
first-name-comparison last-name-comparison))

And we’ll use them to compare two people:

ClojureExamples/src/mbfpp/rso/closure_comparison.clj
 
(​def​ p1 {:first-name ​"John"​ :middle-name ​""​ :last-name ​"Adams"​})
 
(​def​ p2 {:first-name ​"John"​ :middle-name ​"Quincy"​ :last-name ​"Adams"​})
 
=> (first-and-last-name-comparison p1 p2)
 
0

That wraps up our look at using closures to replace state-carrying Functional Interface implementations. Before we move on, let’s discuss the relationship between closures and classes in a bit more detail.

Discussion

There’s a joke about closures and classes: classes are a poor man’s closure, and closures are a poor man’s class. Besides demonstrating that functional programmers probably shouldn’t go into standup comedy, this illustrates something interesting about the relationship between classes and closures.

In some ways, closures and classes are very similar. They can both carry around state and behavior. In others, they’re quite different. Classes have a whole bunch of object-oriented machinery around them, they define types, they can be part of hierarchies, and so on. Closures are much simpler—they’re just composed of a function and the context it was created in.

Having closures makes it much simpler to solve a whole host of common programming tasks, as we’ve seen in this section, which is why classes are a poor man’s closure. However, classes have many programming features that closures don’t, which is why closures are a poor man’s class. Scala solves this problem by giving us both classes and closures, and Clojure solves it by deconstructing the good stuff from classes, such as polymorphism and type hierarchies, and giving it to programmers in other forms.

Having closures and higher-order functions can simplify many common patterns (Command, Template Method, and Strategy to name a few) to such an extent that they almost disappear. They’re useful enough that closures and higher-order functions are one of the new major pieces of functionality in the upcoming Java 8 under the guise of JSR 335.

This is a big change to a mature language that absolutely has to be backwards-compatible, so it’s not an easy task. It’s not one that the stewards of Java undertook lightly; but because higher-order functions are such a big win, it was deemed important to include them. It’s taken years of effort to specify and implement, but they’re finally coming!

For Further Reading

Effective Java [Blo08]Item 21: Use Function Objects to Represent Strategies

JSR 335: Lambda Expressions for the Java Programming Language [Goe12] [3]

Related Patterns

Pattern 3, Replacing Command

Pattern 6, Replacing Template Method

Pattern 7, Replacing Strategy

Pattern 16, Function Builder

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

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