Functional Programming is just programming

Marko Novakovic
5 min readFeb 10, 2023

--

Just Another Tool in the Toolbox, don’t be intimidated

Photo by Emile Perron on Unsplash

Kotlin will be used for examples in this post.

Functional programming has been gaining a lot of attention in recent years, but for many developers, the concept is shrouded in mystery. They’ve heard that functional programming is different from object-oriented programming, that it’s complex, that it’s involved in math, and that it’s difficult to learn. But the truth is that functional programming is just another tool in the programmer’s toolbox. It’s a different way of approaching software development, but it’s not scary.

Let me be perfectly clear, it can be really complicated. Especially if you dive into mathematical concepts behind it and all the scary terms like:
category, functor, endofunctor and the scariest of them all….. monad.

Watch this one:

Monad is a monoid in the category of endofunctors.

WTF?!

But guess what… you don’t need to know all of that. It’s fun, it’s (sometimes) useful and of course… it’s hard.

My point is: once you get used to functional programming it becomes “just a programming”. Sooner you start, the better.

For majority of day to day tasks you don’t need those. Those are “big picture”, if you will, while day to day tasks often utilise more mundane concepts like: immutability, functions as first class citizens, pure functions, referential transparency etc.

I will talk about them in order of complexity.

Lets take a look into couple of those:

Immutability

This is the simplest one and I don’t want to beat the dead horse. Just don’t change values after you assign them. Speaking in Kotlin terms: use val instead of var in 95% of situations. Use var for debugging and in highly controlled environment. Even if you think there are no benefits to this just try it and thank me later.

Functions as first class citizens

Treat functions like “normal” values. Pass them as parameters, accept them as arguments, assign them to variables, combine them.

It is possible to write code without classes. You can have classes in functional programming, it is allowed, but you don’t really need them that often, not just in functional programming but in general. Java, for example, has that stupid rule that everything has to be in a class. Kotlin doesn’t.

Example from Kotlin standard library:

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

map takes function as parameter. Btw, map is functor ;).

Pure functions

Pure functions are functions that operate just within their scope. Pure functions don’t touch anything outside of them. They have no side-effects and only return a value based on the inputs given to them. This makes them predictable and easy to test, as their behaviour is consistent and always the same for a given input. Additionally, pure functions can be composed and reused, making them a powerful tool in functional programming.

Note: It’s not possible to write a program that is useful to humans using only pure functions. This is because many tasks that are important to us, such as displaying output or writing to a file, require side-effects. However, it’s important to minimise unnecessary side-effects in our code, as they can make the code harder to understand and debug. For example, if a program has a class with mutable global variables and multiple functions changing those variables arbitrarily, it becomes difficult to reason about the behaviour of the program and identify the source of bugs.

Takeaway: Minimise unnecessary side-effects in your code by allowing only the necessary ones. This helps to keep the code predictable, understandable, and maintainable.

// Pure function
fun add(a: Int, b: Int) = a + b

// Impure function
var x = 0
fun addAndUpdateX(a: Int, b: Int): Int {
x += a + b
return x
}

Referential transparency

A referentially transparent function is one that always returns the same output for the same inputs, regardless of any state or context outside of the function. It means that you can substitute the function call with its returned value, and the program will have the same behaviour.

A pure function is a type of referentially transparent function that has no side-effects. It means that, in addition to being referentially transparent, a pure function does not modify any state outside of its scope, does not perform I/O operations, and does not depend on any context or global state.

So, while all pure functions are referentially transparent, not all referentially transparent functions are pure. A referentially transparent function may have side-effects, but if it always returns the same output for a given input, it can still be considered referentially transparent.

Referential transparency is a property of a function, whereas pure functions are a type of function.

This also means there is no throwing exceptions. Throwing exceptions violates referential transparency as it introduces non-deterministic behaviour into the program by immediately terminating the function and transferring control to a catch block elsewhere. This makes the program harder to reason about, test and debug, and undermines the predictability of the function’s behaviour. In functional programming, errors are typically handled in a more explicit and predictable manner, such as with the Either/Result pattern, to maintain referential transparency.

Most basic implementation would be Kotlin’s Result type. Returning a Result tells caller of a function that that function can fail so failures are expected and don’t come out of the blue.

fun divide(x: Int, with: Int): Result<Int> =
runCatching { x / with }
// Result is returned because you can't divide with 0 so failure is expected.

More sophisticated version would be Arrow's Either type. It allows you to represent exact type of error that might occur, allowing caller to handle each error accordingly.

object DividingWithZero

fun divide(x: Int, with: Int): Either<DividingWithZero, Int> =
either {
ensure(with != 0) { DividingWithZero }
x / with
}
// Now we know what exact error may happen so we are better prepared to handle it.

In conclusion, functional programming is just another way of writing code, and once you get the hang of it, it becomes a normal and natural part of your programming style. By using techniques such as pure functions, referential transparency, and immutability, you can write code that is more predictable, easier to reason about, and less prone to bugs. While it may take some time to get used to these concepts, the benefits they bring to your code make the transition well worth it. So embrace functional programming, learn from its principles, and see for yourself how it can improve your coding skills and the quality of your code.

Achieve functional programming mastery with consistency. Our habit tracker app helps you focus on daily learning, track progress and keep your winning streak going. Start your journey today and become a functional programming expert.

https://play.google.com/store/apps/details?id=tech.mapps.winthedaywinthelife.android

--

--