Swift combines several programming paradigms like object-oriented, protocol-oriented and functional programming. In this article, we will discuss the last one which provides flexibility in developing applications.
Functional programming methods greatly suit the model layer and wherever the business logic of your application appears. But, note, you shouldn’t use functional programming methods for user interfaces.
The functional declarative approach makes your code more understandable. In addition, the code will be easier to test when it is isolated from modular functions that have no side effects.
Functional programming allows you to minimize side effects and problems from parallelism if you need to maximize the full potential of your multi-core processor.
Consider the following situation:
A family with young kids wants to go on as many rides as possible between frequent bathroom breaks. They need to find which kid-friendly rides have the shortest lines. Help them out by finding all family rides with wait times less than 20 minutes and sort them by the shortest to the longest wait time.
The imperative code works, but it will take a lot of time to carefully look at the algorithm and understand it.
The same issue can be solved by writing just one line of code.
The resulting code is declarative that means it does not require any explanation and is read as a statement of the issue it solves.
This is the essential difference from the imperative code, which is commonly read as the steps that a computer must take to solve a particular issue.
In FP languages, functions are first-class citizens. You treat functions like other objects that you can assign to variables.
Functions can also take other functions as parameters or return other functions. Functions that accept or return other functions are called higher-order functions.
Consider in more detail the most common higher-order functions in FP languages: filter, map and reduce.
In Swift, a filter(_ : ) is a method of Collection types like Swift arrays. It takes another function as a parameter. This is what the function takes.
a filter(_ : ) applies an input array and returns another array. The output of the function returns true.
The Collection method map(_ : ) takes one function as a parameter. It displays an array of the same length after applying this function to each element of the collection. The return type of the associated function does not have to be the same as the type of the elements of the collection.
The Collect Reduce(_: _ : ) method takes two parameters: the first is the initial value of an arbitrary type T, and the second is a function that combines the value of the same type T with an element in the collection to get a different value. type T.
The input function is applied to each element of the calling collection, one after the other, until it reaches the end of the collection and gives the final accumulated value.
Partial functions allow you to encapsulate one function into another.
Let’s give an example:
Here, filter (for : ) takes RideCategory as a parameter and returns a function of type ([Ride]) -> [Ride]. The output function takes an array of Ride objects and returns an array of Ride objects filtered by the provided category.
The idea of a pure function is a basic concept in FP, which allows you to talk about the structure of the program and the results of the test program.
A function is pure if it meets two criteria:
Pure functions are associated with the concept of referential transparency. A program element is a reference transparent if you can replace it with a definition and always give the same result. This makes for predictable code and enables the compiler to perform optimizations. Pure functions satisfy this condition.
Reference transparency is useful when you need to refactor the code and be sure that nothing is broken anywhere. The relatively transparent code is not only easy to test, but also allows you to move the code without checking the implementations.
The last concept to discuss is recursion. Recursion occurs whenever a function calls itself as part of its body function. In functional languages, recursion replaces many cyclic constructs that you use in imperative languages.
When entering a function leads to a call to the function itself, you have a recursive case. To avoid an infinite stack of function calls, recursive functions need a basic case to complete them.