We know FP matters, but what is it really? There are a lot of discussions related to FP superiority and different definitions of it, but simply, it is a style of programming that models computations as the evaluation of expressions or declarations. FP is a declarative programming style, as opposed to OOP, which is categorized as imperative programming.
Theoretically, FP employs the concepts of category theory, which is a branch of mathematics. It is not necessary to know the category theory to be able to program functionally, but studying it will help to grasp some of the more advanced concepts, such as Functors, Applicative Functors, and Monads. We will get into category theory and its relationship with FP later, so for now, we are not going to talk math and we will scratch the surface of FP pragmatically.
Let's start with an example to understand the differences between imperative and declarative programming styles. The following example gives two different approaches to array-element multiplication:
let numbers = [9, 29, 19, 79]
/ Imperative example
var tripledNumbers: [Int] = []
for number in numbers {
tripledNumbers.append(number * 3)
}
print(tripledNumbers)
/ Declarative example
let tripledIntNumbers = numbers.map({ number in number * 3 })
print(tripledIntNumbers)
In the imperative example, we create a mutable array of integers. Then, we give a command to go through all items in the array, multiply each item by 3, and add it to our mutable array. Basically, we order the compiler to follow specific steps. As it is written in an imperative style, when we read it, we will need to trace and follow the same steps, like a compiler, which is not very intuitive.
In the declarative example, we declare how numbers should be mapped, multiplying each number by 3. It may not look that intuitive right away if we do not know the map function and closures, but for now we need to understand the differences between commanding and declaration. This example may not be enough to grasp it. Also, understanding a concept is one thing and applying it is another. That is why we will have more examples in upcoming sections and chapters. In fact, everything in this book will be written declaratively, so we will utilize the concept intuitively.
In FP, functions are the fundamental building blocks, so programs are structured by functions. In OOP, programs are composed of classes and objects. This is a fundamental difference because, in OOP, statements can mutate the state of objects when executed, as opposed to FP, which avoids using mutable states.
FP promises that avoiding mutable states makes it easier to test, read, and understand the code. Although it is a well-known fact that state management is hard and error-prone, it is not easy to avoid mutable states in some cases, such as cocoa development and file and database operations. For now, we consider traditional OOP and FP, and we will get into the comparison and combination of different paradigms in an upcoming chapter.
FP requires functions to be first class. First-class functions are going to be treated like any other values, and can be passed to other functions or returned as a result of a function.
FP requires functions to be able to be formed as higher-order functions that take other functions as their arguments. Higher-order functions can be used to refactor code, reduce the amount of repetition, and to implement domain-specific languages (DSL).
DSLs are languages that are specialized for a particular application domain. Domain-Specific Languages, a book by Martin Fowler, is a great reference for the curious. For more curious readers, declarative Auto Layout DSLs for Swift such as SnapKit and Carthography and a talk given by Rahul Malik at Functional Swift conference on writing domain specific languages (