Object-oriented programming is a paradigm where the focus lies on objects, which can contain data and code. Data is in the form of fields and the code in the form of methods or functions. That gives a developer the freedom to think of their code as entities and the relations between them. Since relations between objects and their data changes over time, the state of an object is essential.
I will cover the functional programming paradigm, and it completely discards the idea of objects. Its goal is to discard state and mutable data. Whenever you input something into a program, the output will always be the same. Thus, the result of a function is only reliant on its arguments. To ensure the function’s result is described only by its inputs, it cannot have any side effects. Furthermore, I will use the functional programming language Haskell as a baseline for the examples.
Since a purely functional program is not allowed any side effects, it cannot interact with the real world. It cannot print to a console, show an image on the screen or gather user input. For a beneficial program, these things are essential, but side effects often cause problems in your code. The goal of the functional paradigm is to create a separation between code that contains side-effects and code that does not. If you know a function’s output only depends on its inputs, it becomes easier to test for correctness. Compared to when a state can influence the result.
Another benefit of functional programming is that functions are first-class data. That means that functions are possible as arguments for functions. A function that takes a function as a parameter is called a higher-order function. That allows for abstractions in a way that makes code shorter, easier to read and less error-prone. Whereas in object-oriented programming, writing complex functions is difficult. In functional programming, you reuse functions to create more complex functions.
The easiest and most elegant example of higher-order functions is the map function. The map function takes two arguments, a list with elements of a certain type
a and a function that converts that type
a to another type
b. The type structure would look like this:
map :: [a] -> [a -> b] -> [b]. Thus, this function would return a list of the new type
-- Type structure of the map function map :: [a] -> (a -> b) -> [b] -- Function to add 1 to every integer in a list map (+1) xs -- Function to double every integer in a list map (*2) xs -- Function to double every integer and then add 1 in a list map ((*2) . (+1)) xs
How you should read the statement
map f xs would be: map the function
f over every element in the list
xs. By using higher-order functions, code becomes easier to read, easier to maintain and more reusable.
map is just one of the standard higher-order functions, alongside more than 200 other functions implemented by its standard library. This gives for a lot of creative freedom when using higher-order functions since often multiple solutions exist for the same problem.
Those concepts are just the tip of the iceberg of what’s possible with functional programming and what advantages it can offer. To sum up, functional programming creates an abstraction between code that contains side effects and code that doesn’t. Combined with higher-order functions, one can write clear, concise and understandable code, all without worrying about side effects.