As a developer, I believe that entering the world of Functional Programming (FP) is like erasing all that you have learned so far and relearning everything, although not from scratch.
This article aims to go through every fine step, introducing and explaining the core concepts of functional programming. We will see how we usually write code in imperative programming and then implement it using the functional programming approach.
In essence, this article should serve as an introduction to the functional programming world for a smooth transition from the usual imperative programming world. So, let’s dive right in.
Note that we will be using the Swift language for our programs/examples throughout the blog. However, even if you don’t belong to the iOS development world, you should be able to follow along smoothly.
What is Functional Programming?
Functional programming heavily emphasizes the usage of functions that contain a set of code/instructions to perform a generic set of operations. In simple terms, Functional Programming (FP) is a paradigm that concentrates on computing results rather than performing actions. When you call a function in a program written with a functional approach, the only significant effect that the function has is usually to compute a value and return it instead of giving it a finite set of step-by-step instructions to get the desired results.
Although, in the functional programming world, behind the scenes, the function is, as usual, using its own set of instructions which eventually uses up the CPU time, allocating and writing onto the memory. However, from the programmer’s point of view, the primary effect is the return value. Basically, it is more like an encapsulation of a usual set of instructions providing you with higher-order APIs which you can use to execute those encapsulated instructions to perform certain operations.
Core concepts in Functional Programming
1. Pure Functions
Any function/method in the functional programming world is a pure function.
Example:
var a = 2
var b = 4
func add(x, y) {
return x + y
}
Notice how the add()
function doesn’t access the value of the variables: a
& b
. Instead, it simply takes its own input parameters: x
& y
, performs the required operation on them, and returns it back to the caller. So, any function in the functional programming world is simply a pure function/method.
2. Immutable Objects
Objects that we deal with in a functional programming language are often immutable (a.k.a. constant or final). So, instead of changing an object’s properties to modify it, you will allocate a new object that just looks like the old one except for the change that’s taking place.
In a nutshell, all the objects in the functional programming world are immutable. This is a key concept when running programs in multi-threaded environments. So, with functional programming implementations, your code is safe.
Note that there are no variables in functional programming by definition, although stored values are still called variables despite being constants.
Let’s look at the previous example again:
func add(x, y) {
return x + y
}
Notice how the inputs to the pure function: x
& y
aren’t modified. When such programs run in multi-threaded environments, it is much safer compared to having variables. We will see this in action in the upcoming sections.
3. Declarative Programming
Functional Programming is a subset of declarative languages that has a heavy focus on recursions. Yes, instead of directly using loops, FP uses recursions to perform an operation that can be achieved with immutability. Loops, on the other hand, require mutability.
Functional programming is otherwise called a declarative language. Ok, what’s declarative now? Well, to understand this, let’s compare it with the imperative approach.
Imperative Vs Declarative Programming

Let’s take an example of adding numbers from 1 to 10.
This is how we do it in imperative programming using a loop:
//Imperative Programming
var total = 0
for i in 1...10 {
total += i
}
print("Imperative: total: ", total); // prints 55
Notice how the value of the variable total
is modified inside the loop which embraces mutability.
On the other hand, functional programming uses the recursive mode, and this is how it can be achieved:
//Functional Programming
func sum(start: Int, end: Int, total: Int) -> Int {
if (start > end) {
return total;
}
return sum(start: start + 1, end: end, total: total + start)
}
print("Functional: total: ", sum(start: 1, end: 10, total: 0)); // prints 55
Notice how recursion, the functional approach, accomplishes the same as the for
loop by calling itself with a new
start (start + 1
) and a new
accumulator (total + start
). It doesn’t modify the old values. Instead, it uses new values calculated from the old.
Consider this program running in a multi-threaded environment where one thread tries to access the variable’s value while another thread mutates it. This might lead the program to an unstable state. The recursive approach or the state of immutability is the safest in such cases. Hence, functional programming is definitely more effective than imperative or traditional programming.
Let’s now move on to understanding Higher Order Functions (HOF) which are a key terminology in the functional programming world. We will also see functional programming concepts in action using Swift HOFs.
Higher Order Functions
A Higher Order Function (HOF) is a function that takes one or more arguments as functions and returns a function as its result.
Let’s take a look at the major HOFs included in Swift’s standard array library –
1. map
The map
method solves the problem of transforming elements of an array using a function.
Example:
Consider building a simple contacts application where we want to list out details of all contacts. We need a list screen for this and, in effect, a list of contacts array in the first place.
Let’s start with creating a struct/data structure to represent the contact object:
struct Contact {
var firstName: String
var lastName: String
var email: String
var age: Int
var duration: Int
}
We now have the data structure ready. Let’s create an array of contact objects next –
let contacts = [
Contact(firstName: "first", lastName: "1", email: "[email protected]", age: 20, courseDuration: 12),
Contact(firstName: "Second", lastName: "2", email: "[email protected]", age: 22, courseDuration: 16),
Contact(firstName: "Third", lastName: "3", email: "[email protected]", age: 30, courseDuration: 22)
]
So, we have 3 objects in our contacts
array. Let’s say we want to derive the full name of the contact i.e. first name followed by the last name. Usually, in imperative programming, this is how we would achieve it:
//Get just the name of the contacts into a separate array
//Imperative Programming
var contactNamesImp = [String]()
for contact in contacts { // for each
contactNamesImp.append(contact.firstName)
}
print(contactNamesImp)
We simply loop through the objects and derive the full name by extracting the values of firstName
& lastName
properties of each object from the contacts
array and append the value into a new array.
But with Swift’s HOF/map
function, we can just pass the function to the method where we define the rules and extract the data in the format we need.
//Functional Programming
let contactNames = contacts.map({
return $0.firstName + " " + $0.lastName
})
print(contactNames)
Here, when the map
method is called on the contacts
array, we simply pass a function that returns only the merged string of firstName
& lastName
properties. Basically, we pass a function to the map
method by calling it on the contacts
array, which contains objects of type Contact
. It returns an array of objects of type String
. Given this HOF is mostly used for the transformation of array objects from one form/type to another form/type, it is rightly called a “map.”
2. filter
The filter
method solves the problem of selecting the elements of an array that pass a certain condition.
Example:
Continuing with our contacts app, let’s say we want to filter out the contacts whose age is greater than 20.
The imperative approach would be something like this –
//Imperative Programming
func filterContacts(aboveAge age: Int) -> [Contact] {
var filteredContacts = [Contact]()
for contact in contacts {
if contact.age > age {
filteredContacts.append(contact)
}
}
return filteredContacts
}
We simply loop through the objects in the array and compare every object with the given condition. We then append the object that satisfies the condition to a newly declared array filteredContacts
.
On the other hand, with functional programming, we can achieve the same by simply calling it upon the array object and passing the condition like this –
//Functional Programming
let contactsAbove20 = contacts.filter({ $0.age > 20 })
print(contactsAbove20)
Note the use of Swift’s HOF equivalent “filter” here. We pass a function to the filter
function which has the rule of validating the age
property to be greater than 20. The filter
method finally returns an array with contact objects matching age > 20.
3. reduce
The reduce
method solves the problem of combining the elements of an array to a single value.
Example:
Let’s assume that all the contacts in our app are readers of this blog. Also, assume Contact
has a duration
property which represents how long a reader reads our blog. We now want to understand how many hours our blog has been read by all of our contacts combined.
In imperative programming, we will loop through all the objects and accumulate the sum in a variable (which is prone to mutability) –
let contactsDurationArr = contacts.map({ return $0.duration }) // We use the map function to derive an array that contains only the duration property of all the objects
var sum = 0
for duration in contactsDurationArr {
sum = sum + duration
}
print(sum)
To write it in a thread-safe way, we can use the immutability approach in FP and use Swift’s reduce
HOF like this –
//Functional Programming
let totalDurationSpentByContacts = contactsDurationArr.reduce(0,+)
print(totalDurationSpentByContacts)
The reduce
method takes 2 parameters –
(i) First represents the starting value from which it should start accumulating
(ii) Second represents the operation (e.g. addition in the above example) which should be performed on the values.
In case you are interested, we have an interesting article on higher order functions and higher order components in React.
Conclusion
We covered the core concepts of functional programming by trying out examples. This should have given you a good understanding of how these functional programming concepts can be easily applied to our implementation/code.
Popular frameworks like React are heavily influenced by functional programming and hence, they have been widely recognized specifically for their functional approach.
Here are the key takeaways –
- Group/Generalize/Break the common set of code into independent functions.
- Do not iterate over the arrays. Instead, use
map
&reduce
. - Use recursions instead of loops.
Now that we have seen the implications of functional programming, it can be heavily leveraged to combine & use along with Reactive programming. This takes us to a whole new world of Functional Reactive Programming (FRP).
Sounds fascinating?
Go ahead to explore our blog on FRP which is an amalgamation of Functional & Reactive programming.
We work with skilled mobile app developers to build amazing products. Do check out our services.