On Functional programming

Summary

A personal note on why functional programming is useful even outside purely functional languages, and how thinking in types, contracts, and transformations can make code easier to reason about.

Published

If I had to give this post a shorter alternate title, it would be: “Why should someone learn functional programming even if they never write a single program?”

I came across a podcast featuring Boris Cherney, the Creator and Head of Claude Code, where he recommended Functional Programming in Scala to everyone because it shaped how he thinks about solving problems. The main point was to think through types, contracts, and boundaries before even writing the implementation.

Fast-forward to my current workplace, where my seniors often point out how little attention functional programming gets in academia. Since I work in fintech and often look for industry examples, I found that Mercury Bank uses Haskell in production and even has an onboarding guide for new engineers.

I have being using Haskell and Scala experimentally and effect-ts professionally since we heavily work with typescript and effect-ts gives us the functional edge that we require while composing monads or using functional constructs like Option, STM, Either etc.

When I talk about structure, I do not mean the data model per se. I mean understanding a piece of functionality before it is written. This connects to clean code principles and the single responsibility principle in SOLID: each function or method should do one thing. In a large program with thousands of functions, the goal is to understand each function from its definition and shape, not from repeatedly reading its implementation.

Take a simple example of reversing a list of integers in Haskell. The first line is the function’s type signature. Haskell can often infer types, but writing the signature makes the contract clear: the function takes a list of integers as input and returns a list of integers.

reverseList :: [Int] -> [Int]
reverseList [] = []
reverseList (x:xs) = reverseList xs ++ [x]

If the next two lines look confusing at first, they are the function implementation written as separate pattern-matching cases.

reverseList [] = []

This is the base case of the function, and it stops the recursion when this condition is met. It says that if the input is an empty list, the output is also an empty list.

reverseList (x:xs) = reverseList xs ++ [x]

This line handles the case where the list is not empty. The pattern (x:xs) splits the list into two parts: x is the first element, and xs is the rest of the list or the tail. The function first reverses xs, then adds x to the end. In simple words, it keeps moving the first element behind the reversed rest of the list until the whole list is reversed. In other languages similar thing could be achieved via recursion.

Visually you can think of it as follows

Decomposition of reversing a list

The same logic in an imperative JavaScript style would look like this:

function reverseList(list) {
  const result = [];

  for (let i = list.length - 1; i >= 0; i--) {
    result.push(list[i]);
  }

  return result;
}

This feels more erratic because the meaning is spread across moving parts. You need to track the result array, the starting index, the loop condition, the decrement, and the mutation inside push. The function still reverses the list, but the reader has to follow the steps in order to understand the idea.

This is why I feel one should think functionally before even picking up the problem. It forces you to ask what the input looks like, what the output should be, and how the result can be built from smaller parts. Before you start writing steps, you start looking for the shape of the problem. That small pause often makes the implementation simpler because you are not rushing into variables, loops, and temporary state.

Functional programming may not be the style you use every day, but it changes how you read and design code. It teaches you to look at types, contracts, boundaries, and transformations before the implementation details take over. Even when I go back to TypeScript or JavaScript, that habit stays with me, and that is the real value I find in learning it.