Functional vs Procedural
February 17, 2024
Overview
Functional and Procedural are among the earliest and most integral programming paradigms. It's nearly impossible to write code without using these paradigms to some extent. Each have their place, but they are often mixed together.
Note that other paradigms exist that should also be considered:
Regarding Functional vs Procedural, there's no quick answer. In modern, feature-rich programming languages like Python, Scala, or JavaScript, the lines between Functional and Procedural paradigms can be blurred. It's possible to write snippets of Procedural code within code that is predominantly Functional or vice versa. Furthermore, it's also possible to write code that is organized in functions and consider it both Functional and Procedural. The main difference is Functional Programming's tenet to keep variables immutable. Continue below for a closer look.
Table of Contents
Functional
Functional Programming is a programming paradigm where each action is separated into its own function. The most important tenet of Functional Programming is immutable variables -- the inability to reassign a value to an already instantiated variable. Functions are first-class citizens, which means they're treated like objects instantiated from classes. They can be stored in variables. This paradigm exists even in a classically Object-Oriented language like Java (where all code must be written in a class). This argument is furthered by the introduction of Lambda Expressions in Java 8. Many modern programming languages have added Functional Programming features like Lambda Expressions, or (to name a few):
map
: A method that belongs to collection classes, such asList
orHashMap
. It applies a function to each element in the collection, returning a new collection with the transformed elements.filter
: Returns a new collection that only contains the elements that match the condition specified (called a predicate).reduce
: Combines the elements of a collection using a binary function to produce a single value.
Utilizing features like these makes code more functional.
Use Case
Functional Programming has many use cases -- it could be used in as many use cases as Object-Oriented Programming or Procedural. The classic use case is data processing, such as ETL (Extract-Transfer-Load), because it's critical for functions and variables to operate in isolation. Immutable variables make it easier to understand the value behind each variable in code at any one of its references.
Procedural
When speaking to colleagues, they will often refer to Procedural code as code that is a single, long, block without functions. However, the technical definition of Procedural Programming does not forsake functions. Procedures and routines are the building blocks of Procedural Programming, and:
- Procedure is essentially a block of code.
- Routine is essentially a function that contains code.
Procedural code can be associated with low quality code. It's true that novices who don't know anything are likely to write in the Procedural paradigm, however, Procedural code is simple and often the most appropriate given a simple use case. If it's organized well into modular functions with good code re-use, then it's indistinguishable from Functional code for that use case.
Use Case
Procedural code has countless use cases, and it can be found everywhere in snippets, at least, and can be used anywhere that Functional code can. Below are some use cases:
- Any part of code where logic is not repeated. It can be argued that separating code into functions that don't ever repeat makes code harder to read overall, since you have to scroll up and down to see the entire logic. Furthermore, you're not getting any benefit of code re-use.
- A project that is intended to be a prototype. In these circumstances, not only is refactoring inevitable, but many components may become abandoned. Spending additional time to modularize code may not add value, if any.
- Scripting
- Lower-level code (closer to hardware)
Comparison
Functional | Procedural | |
---|---|---|
Core Principle | Emphasizes variable immutability and functions as first-class citizens. | Emphasizes clear presentation of sequence of statements or instructions. |
State & Immutability | State is immutable. New variables are created, rather than modifying existing ones. | State is mutable. Variables can be modified at any point. |
Execution Flow | Declarative. | Imperative, but code can be modularized into routines (functions) to make it more declarative. |
Side Effects | Functions should not have side effects outside the arguments provided. | Functions can have side effects that mutate state outside the function and its arguments. |
Testing & Debugging | Easier to test because functions and variables exist in isolation. | Can be challenging to test because external state may depend on other functions outside the scope of the function under test. |
Performance | The need to create more new variables due to variable immutability, and the overhead in the declarative functional features does affect performance. | Generally faster, as operations are straightforward. |
Learning Curve | Steeper due to greater complexity in abstractions. | Lower due to simpler step-by-step presentation of code. However, the languages associated with Procedural Programming may not be relatively easy to learn. |
Typical Applications | Data analysis, data processing, parallel programming. | System-level programming, scripting. |
Popular Languages | Scala, Haskell, Clojure. | C, Pascal, Fortran. |
Table template created by ChatGPT of OpenAI.
Conclusion
Procedural and Functional code can be seen everywhere, especially in modern programming languages. However, in the classical
sense where we're referring to code that was written in languages that don't support Functional features (such as Bash),
then these scripting and system-level languages adhere strictly to the Procedural paradigm. On the other hand, there exist programming
languages like Scala that are rich in Functional features. It's recommended to declare Scala variables with the keyword val
,
making them immutable. Scala was designed with Functional programming in mind, and it's the native language
of Spark, a
large-scale distributed data analytics and processing framework.
To be updated with code examples.