Do you believe that mutable state is the root of all evil? Aficionados of functional programming certainly do, and they have a point. There are many types of bugs that don’t exist in purely functional programs, especially concurrent ones. On the other hand, the world changes with time, and object orientation is a natural paradigm for programs that try to model part of the real world.
While the theories behind the two paradigms are in conflict, it is possible to enjoy the best of both by using mutable state whenever it is necessary but being functional elsewhere. Many imperative and object-oriented languages, such as Java, C++, C#, and Python, now support functional concepts. From the other side, there are many almost-functional languages that also support state changes.
Many books explain how to use functional features together with state changes; Functional design is unique in presenting a holistic view of the development process, not just a particular language.
The first part of the book presents functional concepts, starting from Java and moving to Clojure, a functional descendant of Lisp. This part explains immutability and its benefits, modeling state changes by creating new data structures that share memory with previous ones, lazy evaluation, and the use of software transactional memory as a relatively benign interface to mutable state when really necessary.
The second part compares Java and Clojure solutions to three programming problems. The more complex the problem, the more apparent are the differences between state contained in objects and state passed between functions. A fourth comparison uses the example of an employee database, demonstrating how an object-oriented approach (including inheritance) can be implemented in a functional style and the correspondence between Java interfaces and Clojure’s “defmulti.”
While Clojure is not strongly typed, it does support an elaborate type specification library, which can also be used for runtime checking. This library is used throughout the rest of the book.
Part 3 addresses issues of functional design, and is mainly devoted to the five SOLID principles. While SOLID was developed, and is usually presented, as as set of design principles for object-oriented (OO) programs, Martin shows that they are just as valid (with some adaptations) to functional programming. This is an important lesson for developers who use either paradigm.
Part 4 deals with “pragmatics.” This includes testing, for example, mocking in an OO style and property-based testing (generating random inputs to exercise a program). This is one area where functional programming shines, that is, by removing the need to write complex code just to get the program to a state where the test can start. How to create an interactive graphical user interface (GUI) for a functional program (where the program must effect changes on the screen and respond dynamically to user input) is shown next. Finally, the chapter on concurrency shows that, while functional programs have many fewer problems with it, they are not completely immune.
Design patterns are another area that seems to be completely in the OO domain. Given the discussion of the analogues of classes and inheritance in Clojure, it shouldn’t come as a surprise that, again with appropriate adaptations, they are just as applicable to functional programming. Part 5 demonstrates a number of patterns in Clojure, including Adapter, Command, Undo, Composite, Decorator, Visitor, and Abstract Factory. OO programmers who are afraid that moving to a functional language will deprive them of design patterns, as well as functional programmers who are not aware that they can make good use of them, should take note.
The final part, consisting of a single long chapter, contains a case study of the development process of a larger program: the Wa-Tor game. Even though this is still not a very large program, it is treated as though it were, and Martin goes through a series of steps, making mistakes and correcting them as he goes along. This example is used to put much of the preceding material in the context of one program, showing how they fit together in the development process.
Features of Clojure are presented throughout the book as needed, with much of the information in short footnotes. While the syntax of Clojure is indeed simpler than many other languages, it still has quite a few non-obvious syntactic abbreviations and, of course, a large set of libaries. Glossing over the difference between special forms and functions could be confusing to readers who aren’t used to functional programming. Martin finally acknowledges that the reader needs to have a Clojure reference nearby only three quarters through the book; much of this could have been avoided by supplying a short cheat sheet that collects all relevant information.
If you want to know how to enjoy the benefits of functional programming in a changing world, as part of the whole development process, pick up a copy of this book. It will be worth the effort you will put in!
More reviews about this item: Amazon, Goodreads