Introduction to Functional Programming in Kotlin
Welcome to the functional programming(FP) series. The outcome of this series is to introduce you to the functional programming paradigm with the examples written in Kotlin and solutions from Android development eco-system.
From the author
This paradigm is not new to the programming world. Ever since the adoption of Kotlin by Android Devs, this language has opened up a lot of doors within the Android ecosystem. One such door is functional programming. I have been experimenting it for some time and consider this series as my personal notes
Series pit stops
- f(1) — Basics, Understanding Functions (you are here)
- f(2) — Functions in Kotlin, Pure functions
- f(3) — Function composition
- f(4) - Currying function
Basics
This pit stop is all about introduction to functional programming(FP) paradigm. We’ll take a step back and understand what is meant by function. What problems does it solve, why we preach to Functional Programming.
Pre-requisites
- Basic programming knowledge
- School mathematics
Programmer’s nightmare
Apart from the long-running meetings, tackling opinions, chasing reviews merging tickets, neck pain and caffeine addiction, one of the worst nightmare of every programmer is bugs. It never discriminates an entry-level or advanced programmer. It can be of different forms. That’s why we keep writing tests and also employ people to find and squash them.
For most of us, tackling bug would be like a part of our job or life. But consider if you are writing a program to launch and manage rockets — take a moment and think what will happen if you introduce a bug in the system?
You might kill astronauts and cost millions of taxpayers money. How about the bugs in any of the medical diagnosis or assistive technologies? You’ll put lives at risk.
I hope we agree that bugs can cause serious threats. It’s our responsibility to write safer programs. I understand, programs are written by humans and we do make mistakes. But we should try our best.
Making programs safe with FP
How do we make our programs safe using FP concepts? Some common strategies are
- Avoiding sharing or handling mutable references/variables — if a data can be altered, it’s a threat
- Avoiding complex control structures — this will provide a safe-house to hide or create bugs
- Controlling external effects — a program should depend only on its arguments. It should not depend upon external actions such as printing, saving to the database, logging etc. This way, it’s clear on what the function takes and what it returns.
- Avoiding exception throwing — Exception throwing is once again considered as branching or control structures. This might create a safe-haven for bugs (if not properly handled)
- Making programs simple and easily testable — this can once again be achieved if we separate the external dependencies and keep the functions pure (we’ll see it later)
If you are not aware of FP concepts, this might sound super bizarre to you. You are not alone. We might be reading a lot of ways on how to do the above things better, but avoiding? can’t imagine right?
We’ll be looking most of them in our series (can’t promise 🤞)
Function
To understand what a function is, let’s take a step back to our programming 101. You would have learned that a function or method is a procedure or a routine that performs a specific task. It can have 0 or more arguments and can return none or one value.
In the functional programming world, this definition or explanation does not apply. A function in FP is equivalent to a mathematical function.
It’s time to remember our school mathematics lessons.
A function is a relationship between a source set and a target set. The source set is called the domain and the target set is called the codomain.
Decoding domain & codomain relationship
If you look at the image above, the domain is one set and it’s mapped to another target set aka co-domain, for the function
1f(x) = x + 1
We substitute values from the domain in place of x and the desired result is mapped to the codomain. The mapped values in the codomain are called the image of the function f(x).
Some of the common rules of function mapping are
- There cannot be elements in the domain without any image in co-domain. Whereas codomain can have values that are not the images of a given function (Just like the figure above)
- A value of domain cannot have multiple images in the codomain. Whereas multiple values from the domain can have a common or same image in the codomain
Fun fact: There are other types of functions, such as inverse function and partial function (out of scope for this series ✌️)
If you all the domain set as A and codomain set as B, then
f(x) is a function from A to B. This can also be represented as A -> B.
In programming notation, especially Kotlin considering this series, we represent the function as in Kotlin way: (A) -> B
Function re-defined
Now, if you can see what a function is, nothing but a mapping from the source set to the target set. If you talk programming, then (approximately) it’s nothing but a mapping between the arguments to return an image of value or a function.
If you name the function f(x) = x + 1 as plusOne:
1plusOne(x) = x + 1
Technically, it does not add one. It just produces an image for every value of x. So it’s always good to remember that function definition is different. The naming is not a literal translation and it does not have the same meaning or relationship.
Function Composition
Functions are like legos, meaning — you can use them to build or compose other functions. In other words, function composition is an operation on functions to produce a new function.
For example,
- Consider function1: f(x) = x + 1
- Consider function2: g(x) = 2x
- Our aim, to compose a function h(x) using f(x) and g(x)
The composition of a function f over g is usually denoted as f o g. This is pronounced as f of g or f round g or can be denoted as f(g(x)). Which gives us,
1h(x) = f o g = f(g(x) = f(2x) = (2x) + 1 => 2x + 1
The value of g(x) is substituted in place of x in f(x) ☝️
Hence, f o g = 2x + 1
Fun fact: You can also compose g o f, but f o g and g o f are not equal operations. they may result in the same sometimes though. If we compose g round f for the above functions,
1g o f = g(f(x)) = 2(x+ 1) = 2x + 2
Hence, for the mentioned f(x) and g(x), f o g != g o f
Pictographic representation
If the equations are confusing for you 👇 (you are not alone 🤫)
1Given: x = { 1, 2, 3, 4 }
If you are excited about this, you can draw how g o f would look like the picture above. You’ll definitely see that for the mentioned f(x) and g(x),
f o g != g o f
TLDR;
- Tackling bugs is a serious business. We must always try to make our programs safer
- FP aims to eradicate bugs and some common strategies are following immutability, avoiding side effects, control structures and throwing exceptions
- A function is a relationship between a source set and a target set. The source set is called the domain and the target set is called the codomain.
- Function composition is an operation on functions or sets to produce a new function.
- There is nothing like a function with multiple arguments, but a function may have a product of element sets to produce a new set.
Additional References
If you think you have learned something new or if you like this series or you want to boost my morale to publish the next pit stop fast, please share the post
Thanks a lot for reading this article. Stay tuned!!