Here is where we introduce all of the patterns we cover in the book and give a brief overview of each. This is a great list to skim if you already have a specific problem you need to solve in a functional way.
This section shows how to replace common object-oriented patterns with functional language features. This generally cuts down on the amount of code we have to write while giving us a more concise code to maintain.
Here we replace common types of functional interfaces, such as Runnable or Comparator, with native functional features.
This section introduces two basic types of functional features. The first type, higher-order functions, allows us to pass functions around as first-class data. The second, anonymous functions, allows us to write quick one-off functions without giving them a name. These features combine to let us replace most instances of Functional Interface very concisely.
With this pattern we replace instances of Functional Interface that need to carry around some bit of state—we introduce another new functional feature, closures, which lets us wrap up a function and some state to pass around.
Replacing Command encapsulates an action in an object—here we’ll take a look at how we can replace the object-oriented version using the techniques introduced in the previous two patterns.
Here we carry data using the classic Java convention, a class full of getters and setters—this approach is intimately tied up with mutability. Here we’ll show how to get the convenience of a Java Bean along with the benefits of immutability.
Replacing Iterator gives us a way to access items in a collection sequentially—here we’ll see how we can solve many of the problems we’d solve with Iterator using higher-order functions and sequence comprehensions, which give us solutions that are more declarative.
This pattern defines the outline of an algorithm in a superclass, leaving subclasses to implement its details. Here we’ll see how to use higher-order functions and function composition to replace this inheritance-based pattern.
In this pattern we define a set of algorithms that all implement a common interface. This allows a programmer to easily swap out one implementation of an algorithm for another.
In this pattern we discuss how to replace Null Object and talk about other types of null handling—in Scala, we take advantage of the type system using Option. In Clojure, we rely on nil and some language support to make it more convenient to deal with.
Replacing Decorator adds new behavior to an object without changing the original class. Here we’ll see how to achieve the same effect with function composition.
Replacing Visitor makes it easy to add operations to a data type but difficult to add new implementations of the type. Here we show solutions in Scala and Clojure that make it possible to do both.
This pattern injects an object’s dependencies into it, rather than instantiating them inline—this allows us to swap out their implementations. We’ll explore Scala’s Cake pattern, which gives us a DI-like pattern.
Tail Recursion is functionally equivalent to iteration and provides a way to write a recursive algorithm without requiring a stack frame for each recursive call. While we’ll prefer more declarative solutions throughout the book, sometimes the most straightforward way to solve a problem is more iterative. Here we’ll show how to use Tail Recursion for those situations.
Mutual Recursion is a pattern where recursive functions call one another. As with Tail Recursion, we need a way to do this without consuming stack frames for it to be practical. Here we’ll show how to use a feature called trampolining to do just that.
Filter, map, and reduce are three of the most commonly used higher-order functions. Used together, they’re a very powerful tool for data manipulation and are the inspiration for the popular MapReduce data-processing paradigm. In this pattern, we’ll see how they can be used on a smaller scale.
Functional programming eschews mutability; so instead of mutating a data structure, we take one immutable data structure, operate on it, and produce a new one. Chain of Operations examines the differing ways to do so in Scala and Clojure.
Higher-order functions can create other functions using the Function Builder pattern. Here we’ll show some common instances of the pattern that are built into many functional languages, and we’ll explore a few custom ones.
This pattern caches the results of a pure function invocation to avoid having to do an expensive computation more than once.
Lazy Sequence is a pattern where a sequence is realized bit by bit only as it’s needed. This allows us to create infinitely long sequences and to easily work with streams of data.
Focused Mutability makes a small critical section of code use mutable data structures to optimize performance. The need for this is less common than you might think. Clojure and Scala, backed by the JVM, provide very efficient mechanisms for working with immutable data, so immutability is rarely the bottleneck.
With most languages, it’s impossible to add a new way of doing control flow to the language without modifying the language itself. Functional languages, however, usually provide a way to create custom control abstractions tailored for specific uses.
The Domain-Specific Language pattern allows us to create a language that is purpose-built for solving a specific problem. Using a well-designed implementation of domain-specific language is the ultimate solution for often-solved problems, as it lets us program close to the problem domain. This reduces the amount of code we have to write and the mental friction in transforming our thoughts into code.
3.147.74.211