What this book covers
Chapter 1, Functions, explains the core concept of FP – functions. It introduces function definitions and shows how functions are called. This includes an explanation of all the syntactic elements (types, type signature, function body, etc) and their role. Along the way, it also introduces a number of built-in types and functions.
Chapter 2, Algebraic Datatypes, introduces Haskell’s mechanism for user-defined types – Algebraic Datatypes (ADTs). We build up from simple forms of ADTs such as enumerations and records to their full generality, and we learn the different elements of ADT definitions – the type name, the data constructors, and their fields. We see how ADT values are created and how they are taken apart by pattern matching. Finally, we see how ADTs can be parameterized over other types.
Chapter 3, Recursion, presents the functional programming alternative to loops. Recursive datatypes are a natural mechanism to express data structures of arbitrary size, and recursive functions are the way to process these functions. We will focus in particular on structural recursion as a principled use of recursion but also cover alternative recursion patterns.
Chapter 4, Higher-Order Functions, explains how repeated patterns in function definitions can be abstracted over, notably by abstracting over higher-order parameters. Special attention goes to commonly used higher-order library functions such as map
, filter
, foldr
, and foldl
.
Chapter 5, First-Class Functions, covers a range of language features and mechanisms that facilitate function-oriented programming, where we program at the level of functions rather than plain values.
Chapter 6, Type Classes, presents Haskell’s unique mechanism for supporting ad-hoc overloading–type classes. It explains what ad-hoc overloading is and how functions give rise to polymorphic type signatures with type class constraints. Then, we will see how predefined type classes can be instantiated for user-defined types and how new user-defined type classes can be created. Finally, we cover several user-defined type classes and their use in standard libraries.
Chapter 7, Lazy Evaluation, reveals Haskell’s unique evaluation mechanism – lazy evaluation. It shows how lazy evaluation works and improves upon both call-by-value and call-by-name. Then, it shows how to use the strategy to your advantage by using lists as iterators that supply their elements on demand. Finally, the chapter also points out a pitfall of the mechanism – the build-up of thunks.
Chapter 8, Input/Output, explains Haskell’s unique way of interfacing with the outside world – its I/O mechanism. First, the chapter explains why the existing approach of other languages is problematic due to the lazy evaluation strategy and the language’s purity principle. Then, it presents the Haskell solution using the I/O type and >>=/return
operators. Then, it introduces the do
notation as a more user-friendly notation for I/O steps. Finally, it covers common I/O operations.
Chapter 9, Monoids and Foldables, introduces the notion of foldables. These are collections that support a similar range of frequently used operations. Along the way, we learn that Haskell has a mechanism to abstract over parts of types, called type constructors, and uses algebraic concepts such as semigroups and monoids to design highly general algorithms.
Chapter 10, Functors, Applicative Functors, and Traversables, leads us further into the hierarchy of type classes for type constructors. We will first consider functors – data structures that can be mapped over. Then, we will move on to applicative functors, which can merge multiple data structures, and finally, we will see traversables as data structures that can be mapped over with side effects.
Chapter 11, Monads, introduces the king of the type constructor hierarchy – monads. We will first cover two well-known examples of monads – the maybe monad for failure and the state monad for state passing. Then, we will generalize from these examples and present the Monad- type class. Finally, we will present a number of additional examples of monads.
Chapter 12, Monad Transformers, shows how the functionality of different monads can be combined into a single monad. The chapter covers the monad transformer mechanism and gives a range of commonly used instances. Then, it shows how applications can abstract over the implementation details of monads and monad transformers, with monad subclasses. Finally, we will see how the same monad transformers can be combined in different ways to achieve different behavior.
Chapter 13, Domain-Specific Languages, documents a powerful problem-solving technique that is appropriate when a range of problems have to be solved within the same problem area – domain-specific languages (DSLs) embedded in Haskell. First, we will see several examples of DSLs that are tailored to different problem areas. Then, we will focus on implementation techniques for DSLs. The most general and flexible approach is deep embedding. It contrasts with shallow embedding, which is a more lightweight and efficient technique.
Chapter 14, Parser Combinators, introduces parser combinators, a lightweight and convenient DSL in Haskell for parsing. It covers what parsing is and where it is used. Then, we will see a basic definition of parser combinators to get a good idea of how they work. From there, we will move on to the industrial-strength Parsec library. Finally, we see how to tackle common parsing problems with parser combinators.
Chapter 15, Lenses, presents an exciting approach to the mundane activity of data access in nested datatypes. First, it demonstrates the basic approach for records that is built into Haskell and identifies its disadvantages. Then, it presents the concept of lenses as a much more convenient alternative for both reading and updating fields. We will show that lenses not only compose trivially to reach deep into data structures but also can be used to seamlessly define virtual fields. Finally, we cover the main lens combinators provided by the well-known lens
library.
Chapter 16, Property-Based Testing, covers property-based testing, the powerful testing technique pioneered by Haskell’s QuickCheck library. It explains the advantages of property-based testing over unit testing. Then, it shows how to write properties and examine test outcomes. Then, it shows you how to test your own ADTs by writing generators for them. Finally, shrinking is used to drill counterexamples down to their essence.