Functional Programming Fundamentals with Scala Examples
What is the Programming Paradigm?
Programming paradigms are just a set of instructions about how to write and structure a program. There are two main branches of programming paradigms.
Declarative Paradigm:
Declarative programming paradigm focuses on computer programs that express the logic of a computation without describing its control flow. To summarize, it describes what the program must accomplish instead of how.
Imperative Paradigm:
Imperative programming paradigm focuses on computer programs that describe the exact behavior of how programs should act. To summarize, it describes how a program operates in detail.
What is Functional Programming?
Functional programming (FP) is a declarative paradigm, it’s just a way of writing and organizing your code with set rules that enforces you to in a specific way.
As like everything, it originates from Mathematics. Name functional in FP not to be mixed with computer science concepts functions, methods, procedures or subroutines. It comes from the mathematical concept of functions and aims to mimic them. Mathematical functions are representations of mapping between inputs and outputs. This is reflected in the Functional Programming paradigm where functions are implemented as mappings from inputs to desired outputs. In the following section, properties of the paradigm which originated from mathematical functions are discussed in detail.
Properties of Functional Programming
In this section, we will go through the properties of Functional Programming with code examples.
Immutability:
If a variable is set, its value shouldn’t ever change. This is very tangled with other properties of FP like determinism, referential transparency and eliminating side effects. In functional programs, we always work with the pass by value principle for functions.
In the example above, we used a mutable map object that multiplies its value by 2 for each call from its previous state. This can cause unwanted results because since we might forget this and call this function someplace else, it would be hard to debug too.
With this version, it’s much easier to test and debug because there is no mutability.
Deterministic:
Deterministic property of Functional Programming implies that same inputs must produce the same results no matter what.
Referentially Transparent:
Referential transparency is being able to replace function itself with its respective value.
No Side Effects:
Side effects are operations that change some sort of state outside of its local environment. (mutating global variable, mutating a parameter that passed with its pointer, printing to console, inserting data to database)
This is a bit hard to understand at first so let’s look at a real life example.
NOTE: This part is only focused on explaining what the side effect is, not how you fix it with FP.
Scenario: Imagine you have a Baklava store. If you don’t know, baklava is a heavenly made dessert from Turkey. You have an async api and you use a database to store the price of every type of baklava flavor you have. You also have a crawler program that checks your competitors’ baklava prices to ensure that you offer a price that at least matches the cheapest price of your competitors, but you can still set the price manually if you want too.
Note: If this was a real life example our getter and setter functions would have a latency due to network or db queue.
You wanted to increase your lemon flavored baklava so you called the setPrice function to set the price from 10.99 to 11.99. Right after that you ran your crawler program so you wanted to be sure that your new price is still lower than the competitors’. But after some time you realized that you still sell your lemon flavored baklava for 11.99 not for 11.3 like it should be.
You wanted the program to work in this order.
But due to the async nature of your api and unforeseen latencies, you end up with this order.
Means you first get your old price (10.99), then set the new price (11.99) so when you compared with the minimum price of your competitors’ it was 10.99 < 11.3 instead of 11.99 > 11.3.
Lazy Evaluation:
Lazy evaluation is not evaluating an expression until it’s required. In FP you always try to delay the execution of any computation to the last minute, this can result in really noticeable performance improvement on top of making functions pure functions.
This function will wait a second every time it’s called even though we won’t use this variable 90% of the time.
In this program, lazyVariable won’t evaluate unless it’s called and it won’t sleep for a second. This is 90% of the time so if this was an api, we will save a total of 9 seconds for every 10 requests.
First Class And Higher Order Functions:
First class means that functions are treated as variables and passed to other functions as parameters and higher order functions are functions that take other functions as arguments and/or return functions. A programming language must support this property to be used in the FP paradigm.
This is the most complex part of this blogpost so don’t feel bad if you can’t understand at the first try. I advise you to try it step by step to understand it fully.
Pure Functions:
For a function to become a pure function it must satisfy all of the other properties we discussed above. Simply a pure function is deterministic, referentially transparent and will not cause any side effects.
Advantages of Functional Programming
Prototyping and Refactoring:
It is especially beneficial to use FP while prototyping in early stages of development. The reason is that Functional Programs focus on what a program should do instead of how to do it. It makes it easy to change the implementation or try different ways to see which one works better with minimal effort.
Testing:
It is really straightforward to write tests in FP because functions are always deterministic. Also you don’t need to set up complex systems to imitate the state of the program because functions are independent of each other.
Concurrent Programming:
Since FP forces you to write immutable code without side effects, it provides a natural advantage for parallel programming. Like in the testing case, functions are independent of each other, even the same functions between different calls.
Disadvantages of Functional Programming
Learning:
It has a steep learning curve that makes it hard to get a grasp of functional programming at first. Its foundations rely on mathematics, most of its concepts like monads or functors can be hard to use without having prior knowledge in mathematics.
Unattainable:
It’s almost always impossible to write a 100% pure functional program and the reason is mostly side effects. As you learned, side effects can be as simple as printing some message to console to change some documents in a database. It may not make sense to write such program. Real-life applications tend to have two parts. While one part is purely functional, the other part of the application can use non-functional practices. With the FP mindset, the goal of developers is to extend purely functional parts of the applications as much as possible.
Thank you for the time you spend on reading my article.