Preface
This is a book on functional programming patterns using Scala. Functional programming uses functions as basic building blocks. These are functions that don't have any side effects. This challenges our notions about how to write programs. The order of execution for such functions does not matter. We get to reason about them in a referentially transparent manner. This can be a big help in proving correctness. It feels just like a plain arithmetic problem, where (2+2)*(3+3) always equals 24. You may evaluate the expression as 2+2 first, or as 3+3.
I got to know about the Unix culture early in my career. The Unix philosophy relies on pipelining small programs, each doing functionally one and only one thing. One can connect these processing nuggets together. In addition to these hundreds of ready-made building blocks, you could write your own too. These could be easily connected in the pipeline. These pipes and filters were a deeply influential concept as a whole. When I saw Scala's combinators, options, and for comprehensions, I knew I was looking at pipes and filters again. The nuggets in this case were Scala functions instead of Unix processes. Understanding functional programming gives you a new perspective on your code and programming in general. The other aspect of pipelining is that you tend to reuse them intuitively, and you also write less. Though you iterate lines of a text file in a Unix shell pipeline, you don't write any for loops. These are done for you. You just specify which lines pass your criteria or how to transform these lines or both.
Scala allows you to do just that—albeit in a somewhat different form. You don't need to write a for loop, and you keep away from loop counters. Instead, the language invites you to write for comprehensions. Immutability is an actively advocated rule of thumb. Another is avoiding side effects. Scala advocates both. As you probably know, immutability paves the way for more robust concurrency. Why are these so important? Simple, we need to reason about code. Any strategy that makes this activity controlled and simpler is a godsend! Does going down the immutable route mean we end up doing too much copying? How would this Copy On Write measure up against large data structures? Scala has an answer for this in the form of structural sharing.
One-liners are very popular as they get a lot done in a line of code. Scala features allow you to compose such one-liners. Instead of reiterating the same collection, you can do it in one elegant expression. For example, creating an immutable class with constructor and equality comparison defined that is bestowed with destructuring powers is just a one-liner. We just define a case class. There are many situations where Scala one-liners save a lot of programmer time and result in far less code. Combinators such as map, flatMap, filter, and foreach are composed together to express a lot of logic in a one-liner. How does it all affect a program design and design patterns? Here are a few illustrative cases. The singleton design pattern is used to ensure that only one instance of a class could ever exist. Null Objects are specialized singletons that are used to avoid nulls and null checks. Scala's Options give us a similar functionality. Scala's object keyword gives us ready-made singletons. Singletons are specialized factories. A factory creates objects. Scala's syntactic sugar give us a very succinct way to use the apply factory method.
The command design pattern encapsulates an object as a command. It invokes a method on the object. Scala has parameters by name. These are not evaluated at the call site but instead are evaluated at each use within the function. This feature effectively replaces the command pattern with first-class language support. The strategy pattern encapsulates algorithms and allows us to select one at runtime. In Java, we could express the strategy as an interface and the varying algorithms as concrete implementations. Scala's functions are first-class objects. You can pass functions around as method arguments and return values. Functions can be very effective substitutes for the strategy pattern. The ability to define anonymous functions is really helpful here. The Decorator pattern is needed at times. It can be used to decorate (that is, extend) the functionality of an object without modifying it. Some design plumbing needs to be done though. Scala's stackable traits can express the same design very elegantly. One use of the proxy design pattern is for implementing lazy evaluation. When some computation is expensive, we do it only when needed.
As we are very familiar with eager evaluations, we create a list in memory and think it is fully realized. However, just like eager lists, there are lazy lists too. If we think of a typical OR (||) conditional statement, if the left operand is true, the right is not evaluated. This is a very powerful concept. Scala's streams provide lazy lists.
Chain of responsibility is another handy pattern that is used to decouple the sender of a request from its receiver and allows more than one object (a chain of objects) to handle a request. If any object in the chain is not able to handle the request, it passes the request to its next object in the chain. Scala's partial functions fit this bill nicely. We can chain partial functions with Scala's orElse operator to realize the pattern. When we write code, we need to handle errors. Scala's Try/Success/Failure again allows us to write pipelines, and if any piece of the pipeline is an error, rest of the pipeline processing is skipped.
We will look at all these concepts in greater detail. We will set up a problem, look at the traditional Java solution, and then see how Scala changes the game.
Welcome to the Scala wonderland!