Building blocks of functional programming in Swift

The first thing to realize is that Swift is not a functional programming language. At its core, it will always be an object-oriented programming language. However, since functions in Swift are first-class citizens, we can use some of the core techniques. Swift provides some built-in methods to get us started.

Filter

The first method we are going to discuss is called filter. As the name suggests, this method is used to filter elements in a list. For example, we can filter our numbers array to include only even numbers:

var evenNumbers = numbers.filter({ element in
    element % 2 == 0
}) // [2, 4]

The closure we provide to filter will be called once for each element in the array. It is tasked with returning true if the element needs to be included in the result and false otherwise. The preceding closure takes advantage of the implied return value and simply returns true if the number has a remainder of zero when being divided by two.

Note that the filter does not change the numbers variable; it simply returns a filtered copy. Changing the value will modify the state, which we want to avoid.

This method provides us with a concise way to filter a list in virtually any way we want. It is also the beginning of building up a vocabulary of transformations, which we can perform on data. One could argue that all applications just transform data from one form to another, so this vocabulary helps us achieve the maximum functionality we want in any app.

Reduce

Swift also provides a method called reduce. The purpose of reduce is to condense a list down to a single value. Reduce works by iterating over every value and combining it with a single value that represents all previous elements. This is just like mixing a bunch of ingredients in a bowl for a recipe. We will take one ingredient at a time and combine it in the bowl until we are left with just a single bowl of ingredients.

Let's take a look at what the reduce function looks like in our code. We can use it to sum up the values in our number array:

var sum = numbers.reduce(0, combine: { previousSum, element in
    previousSum + element
}) // 15

As you can see, reduce takes two parameters. The first parameter is a value with which to start combining each item in the list. The second is a closure that will do the combining. Similar to filter, this closure is called once for each element in the array. The first parameter of the closure is the value after combing each of the previous elements with the initial value. The second parameter is the next element.

So the first time the closure is called, it is called with 0 (the initial value) and 1 (the first element of the list); it then returns 1. This means that it is then called again with 1 (the value from the last call) and 2 (the next element in the list) returning 3. This will continue until it is combining the running sum of 10, with the last element 5, returning a final result of 15. It becomes very simple once we break it down.

Reduce is another great vocabulary item to add to our skill-set. It can reduce any list of information into a single value by analyzing data to generate a document from a list of images and much more.

Also, we can start to chain our functions together. If we want to find the sum of all the even numbers in our list, we can run the following code:

var evenSum = numbers.filter({$0 % 2 == 0}).reduce(0, combine: {$0 + $1}) // 6

Now, we can actually do one more thing to shorten this. Every arithmetic operation, including addition (+) is really just another function or closure. Addition is a function that takes two values of the same type and returns their sum. This means that we can simply pass the addition function as our combine closure:

evenSum = numbers.filter({$0 % 2 == 0}).reduce(0, combine: +) // 6

Now we are getting fancy!

Also, keep in mind that the combined value does not need to be the same type that is in the original list. Instead of summing the values, we could combine them all into one string:

let string = numbers.reduce("", combine: {"($0)($1)"}) // "12345"

Here I am using string interpolation to create a string that starts with the running value and ends with the next element.

Map

Map is a method to transform every element in a list to another value. For example, we can add one to every number in the list:

let plusOne = numbers.map({ element -> Int in
    return element + 1
}) // [2, 3, 4, 5, 6]

As you can probably guess, the closure that map takes is called once for each element in the list. As a parameter, it takes the element and is expected to return the new value to be added to the resulting array.

Just like with reduce, the transformed type does not need to match. We can convert all of our numbers to strings:

let strings = numbers.map {String($0)}

Map is incredibly versatile. It can be used to convert a list of data into a list of views to display the data, convert a list of image paths to their loaded images, and so on.

The map method is a great choice to perform calculations on each element of a list, but it should be used only when it makes sense to put the result of the calculation back into a list. You could technically use it to iterate through a list and perform some other action, but in that case, a for-in loop is more appropriate.

Sort

The last built-in functional method we will discuss is called sorted. As the name suggests, sorted allows you to change the order of a list. For example, if we want to reorder our numbers list to go from largest to smallest:

numbers.sort({ element1, element2 in
    element1 > element2
}) // [5, 4, 3, 2, 1]

The closure that is passed into sorted is called isOrderedBefore. This means that it takes two elements in the list as input and it should return true if the first element is to be ordered before the second element. We cannot rely on the closure to be called a certain number of times, nor the elements it will be called with, but it will be called until the sorting algorithm has enough knowledge to come up with a new order.

In our case, we return true any time the first argument is greater than the second argument. This results in larger elements always coming before smaller elements.

This is a great method because sorting is a very common task and often data will need to be sorted in multiple ways, depending on the user's interaction. Using this method, you could design multiple sorting closures and change the one being used based on the user's interaction.

How these affect the state and nature of code

There are more built-in functional methods and we will learn to write our own in the next chapter on generics, but these are a core few to help you start thinking about certain problems in a functional way. So how do these methods help us avoid state?

These methods, along with others, can be combined in infinite ways to transform data and perform actions. No matter how complex the combination is, there is no way to interfere with each individual step. There are no side effects because the only inputs are the result of the preceding step and the only outputs are what will be passed on to the next step.

You can also see that complex transformations can all be declared in a concise and centralized place. A reader of the code doesn't need to trace the changing values of many variables; they can simply look at the code and see what processes it will go through.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset