Understanding functional programming
Functional programming has a long history, and it is interesting to note the extent to which its fundamental principles are exerting an increasingly large influence on other, much younger programming styles. As programs become more complex, with increased use of multiple processing cores (and therefore parallel processing), concepts that were originally reserved for the likes of Haskell and Lisp are making an impact across a wide range of programming scenarios and languages.
We could have a lively and entertaining debate about the one true meaning of the functional in functional programming (which we shall henceforth abbreviate to FP), but space is limited, and a debate in a book tends to get a bit one-sided, so instead we'll need to settle for a somewhat fuzzy definition of what FP actually means.
The following aspects of FP are absolutely core to its nature, however:
- A pure function always returns a value, and will always return the same value for any given input.
- That's all a pure function does. It has no side effects; the return value is its only way to contribute to the program.
- A function may be passed to another function among its arguments, and a function may also be returned by another function. Functions are said to be first-class entities in this case, and functions that either require a function argument or return a function are called higher-order functions.
- A function's arguments and return value define its type. Functions are typed just like Int, String, or [String: Float] types.
Having listed the defining characteristics of FP, there are other aspects that are so strongly associated with it, we can't possibly leave them unmentioned.
Functional programs allow only single assignment. Once we have declared a to be 3, it stays that way forever, and is said to be immutable. We've seen this already with Swift's let statement. The passing of arguments to other functions is always by value; that means a copy is made and passed to a function that only has access to that copy.
Purely functional programs are stateless; each function deals only with the data it is given, and returns a value.
Rather than iterative loops, FP tends to encourage (or enforce) the use of recursive algorithms. Similarly, the process of applying a function to a list of objects, called the map function, is an alternative to the iterative for myObject in myObjectsArray {...} approach of OOP, and this feature is now also a part of the Swift language. In the next chapter, we will see what map, and its sibling, reduce, can do for us.
FP makes use of anonymous functions, which are blocks of code that can be passed around without being defined as a named function first. This is an important part of using higher-order functions. Anonymous functions are called closures in Swift, and in the next chapter, we will look at them in some depth. They're seriously cool.
So here is a calm, sober, subjective-but-sincerely-held opinion:
Time spent getting through the rather alien-seeming ideas around FP will dramatically increase your ability to leverage many of the features available in Swift that are not available in most languages used for writing desktop and mobile applications. Building up an understanding of how FP works, and what advantages it brings, is one of the single most important ways you can improve your skills in programming with Swift. It's also huge fun.
However, we cannot write purely functional code for macOS, or at least not throughout an entire app.