Basic building blocks of Functional reactive programming(FRP): IOS

Control your code’s Side Effects

Every massive structure has basic units that come together and make sense to the whole structure. For example, bricks, cement, paint, concrete, and so on make up basic building blocks on a building. Similarly, before we go ahead and discuss the massive field of functional reactive programming, it would be great if we understood the basic building blocks that will come together so that the massive applications that we create make sense.Let’s have a look at some of the basic building blocks of FRP, starting with event streams.

Event streams

An event stream can be defined as a sequence of events happening over time; you can think of it as an asynchronous array. A simple description of an event stream is shown in the following diagram:

As you can see, we have expressed time on the arrow aligned from left to right, moving forward to the right, and events happen over time. The coloured bubbles (indicated by their names) drawn at intermittent intervals on the timeline represent the events. We can add an event listener to the whole sequence and whenever an event happens, WE CAN REACT TO IT by doing something; that’s the main idea!

We have numerous other types of sequence in Swift, for instance, arrays:

Suppose we have an eventStream array:

var eventStream = ["1", "2", "abc",  "3", "4", "cdf", "6"]

Let’s try to compare eventStream to an array; arrays are sequences in space, which means all the items in the eventStream array now exist in memory; on the other hand, eventStreams don’t have that property. Events might happen over time and you won’t even know all the items that might happen and when will they happen.

So, if we have to relate between an array and event stream, then we can assert that if the [“1”, “2”, “abc”, “3”, “4”, “cdf”, “6”] values happen over a period of time and don’t just exist in memory from scratch, the preceding array will act like an event stream where an event “1” might happen at 1st second, event “2” may happen at the 4th second, event “abc” might happen at the 10th second, and so forth.

Here, note that neither the time at which the event is happening nor the type of event is known beforehand. The events are just addressed as they happen. The nice thing with event streams is that they have functions similar to arrays.

So let’s say that our problem is to add all the numbers in the given array.

Solution to arrays: As you can see, the elements in the array are not numbers; they are strings, so we have to do some transformations here and go over a loop to filter out the strings that cannot be converted to a number and then add the rest if they are a valid number. We might loop over the array using a for loop, but while we traverse over the array inside a for loop, we will use the map and filter operations to provide a solution to the problem, which is the functional approach:

Step 1:

let result = eventStream.map{// inside map we will parse the array //elements to convert all the integer compatible elements to integers
}

Step 2: Filter all the elements that are not numbers from the resulting array from step 1. So, the statement in step 1 can now be extended like this:

let result = eventStream.map({// inside map we will ... to integers
}).filter {
// filter all the non integers to form a pure integer array
}

Step 3: Add all the integer values to get the sum by extending step 2 with reduce. So, the same statement can be written as follows:

let result = eventStream.map({// inside map we will ... to integers
}).filter {
// filter all the non integers to form a pure integer array
}.reduce(0,+)

All those functions are called Higher-order functions, and the process of extending the functionality using dot notation in a single line is called Chaining.

We can do the same thing with event streams, the only difference being the availability of events at intermittent time intervals. As a result, processing of the event stream will take its own time and the result will not be populated instantaneously as it will be while working with an array.

State

To be precise, we need to understand more about the shared mutable state. Before we can relate this term to programming concepts, let’s try to come up with a definition in generic terms; consider an example.

Suppose you bought a car and on the first day, you started the car and went for a drive. Everything just runs fine and smoothly. To start the car, you insert the key and rotate it clockwise; in the background, the spark plug ignites a controlled flow of fuel and your engine comes to life. This state of your car is the started state. You then switch the gear to drive mode and press the accelerator with your right foot. Voila! The car starts moving and now you can call this state of your car running and eventually you will stop your car at the destination, and then the state of the car will change accordingly.

So you noticed that your actions or inputs, engine ignition, pistons running, and so on — the sum of all the activities and processes either in foreground or background — comprise the state of the car and since, it can change with respect to several background processes and user actions, it is mutable:

So many factors govern state of the car at a given point in time, and it is difficult to control the state of the car sometimes and then you have breakdowns! Well that’s something for the car mechanic to worry about.

Mapping the same concept to the apps we build, each and every application has a state at any given instance of time. The application might be fetching data from a web service in the background, playing a song in the media player, responding to user inputs, and such — all these actions or processes (synchronous and asynchronous) at any given point in time are collectively termed the state of the application and unlike a car mechanic, it is our responsibility to manage the state of an app at any instance in time.

Side effects

Since you are now aware of the shared mutable state in generic and programming worlds, you can pin down most issues with those two things to side effects.

Every function works in its scope to take an input, apply some logic, and generate an output; functions can have no inputs or outputs as well. For example, a function printing out the state of an object. Side effects occur when the state of the system changes because of the execution of a function. For example, suppose a function named addAndStoreValue() adds two integers and stores the result in the local database and raises a notification for the view to refresh in order to reflect the resulting value, then that change in the view will be the side effect that this function causes because of its execution. In other words, the state of the app changed once the function was executed. This change is called a side effect.

Any time you modify data stored on disk or update the text of a label on screen, you cause side effects.

You must be thinking that side effects are not bad at all; actually, that is the reason we code and execute our programs. We want the state of the system/apps to change once the program has executed. You don’t want your program to run and bring no change to the state of the mobile right? Who wants such an app? Imagine running an app for a while and causing no change in the state of the system at all, pretty useless, aye!

So from the discussion so far, we have understood that causing side-effects is desired, then what’s the issue?

The issue with side-effects is that we want to control the side-effects and hence predict the state of the app once a function has finished execution. We want to control the execution to cause side-effects in a predictable manner and forecast the state of the device once our app starts running. We also need to segregate our app in modules to identify which pieces of code change the state of the app and which pieces process and output data.

RxSwift addresses the preceding issues by making use of declarative coding and creating reactive systems, both described earlier. We will delve deeper into these concepts in upcoming sections.

Functional programming mostly avoids side-effects; because of this the code becomes more testable and hence writing robust code becomes easier.
A well-written app differentiates code that causes side-effects from the rest of the program; as a result of this testing the app becomes easier, extending the functionality becomes clear, refactoring and debugging become a straightforward process, and maintaining such a code base is hassle-free. RxSwift serves a great deal in order to isolate side effects as expected.

Immutability

Before we discuss immutability, let’s start by answering a question: What datatype is more secure to work with in a multithreaded environment?Variable datatype, which can be changed over time, or Constant datatype, which cannot be changed once populated. Let me try to explain the question with the help of an example — suppose you had a variable dictionary of position coordinates:

var locationDictionary = ["latitude": 131.93839, "longitude": 32.83838]

locationDictionary is the location of the user that gets populated once every five minutes; this locationDictionary will, at some time in the near future, be converted to a locations JSON object and synced to the backend (an API pushing data to the server) from where the location will be picked and shown to some web view or it may be used for any other real-time location update purpose.

Imagine working with this location dictionary in a multithreaded environment; since it is a variable, locationDictionary can be updated anytime by any thread at any given moment in time, resulting in faulty location updates. You might end up sending unwanted or corrupt data to your API.

Simple fix: Make the variable a constant, and you can rest assured that once the value is populated in the dictionary, it will not be altered and hence you will have reliable results.

From the preceding discussion, we can now define an immutable object and what a mutable object is: an immutable object is an object that cannot be changed over the course of its existence or scope, whereas a mutable object can be changed within its scope.

Functional programming promotes the concept of immutability; remember the example:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let numbersLessThanFive = numbers.filter { $0 < 5 }

Did you note how we can create a new numbersLessThanFive array as a constant rather then a variable? Try creating a similar array with imperative programming; can you have a constant array ? Let’s try doing that:

var numbersLessThanFive = [Int]()
for index in 0..< numbers.count
{
if numbers[index] < 5
{
numbersLessThanFive.append(numbers[index])
}
}

The process is not only wordy but involves lossy data flow in the program, as any other thread executing in the memory can now change the values in this array since the array is now a variable. Working with immutable data streams is encouraged in any programming language, especially when you are trying to build complex applications that might spawn myriads of threads:

Multiple threads working on the copies of same data
Since one aspect of functional reactive programming comprises a lot of functional programming, working with immutable data comes naturally while modelling logic in the FRP way and hence half the problems are addressed from the start.

Well it is not as simple as it looks, but constant practice will make you perfect. You can practice and master FRP principles and concepts following my book here.

Since immutable objects cannot be changed over time during execution, it means they come at an cost and might not be reusable in certain scenarios. You have to dispose an immutable object or populate a new one if the current one does not fit the context. As you can see, there are certain drawbacks while working with immutable data types, but as intelligent programmers, we need to strike the right balance and make logical choices while modelling our data types:

From the preceding diagram and our previous discussion, it is quite clear why working with mutable state can cause non-deterministic behavior in your program code. As mentioned earlier, we need to strike a balance between both to maintain a shared state to suit the problem at hand. More immutable data types mean more deterministic behavior.

In the next blog I will talk about foundations of RxSwift, till then stay tuned and enjoy reading:)

To have a solid grasp over reactive concepts and write IOS applications in RxSwift you can find the link to my book Reactive programming in Swift 4

Thanks for reading, please share it if you found it useful :)