Functional Programming
Computations are evaluation of mathematical functions.
- Based on the above foundational statement.
💡Although, (mathematical) computations by nature are mathematical functions, so the above statement may sound self-referential. But, computations here mean execution of steps by a computer. These do involve evaluation of mathematical functions generically, but this aspect is more emphasized in functional programming.
Operating within these paradigm means enforcing practices which prioritize this aspect.
- Declarative paradigm.
- Functions are first-class citizens. They can be assigned to variables and passed as an argument.
Functional programming implements concepts from Mathematical dendron://math/analysis.functions in a way which avoid #surprises and promotes concise and readable code.
Components
Pure functions
Functions which are dendron://math/analysis.func.char.idempotent and dendron://math/analysis.func.char.deterministic.
They do not rely on or modify external state, making them easier to test and reason about.
Higher-order functions
Functions that take other functions as arguments or return them as results.
Lambda
Lambdas are expressions used to write concise anonymous functions.
Foundational Principles
The declarative nature might seem like a simple syntactical sugar, functional programming shines by combining this declarative approach with other principles and features.
Additionally, passing functions as arguments ensures lazy evaluation. That is, the functions are not evaluated until their results are absolutely needed.
Immutability
Functional programming emphasizes immutable data structures, where once a value is assigned, it cannot be changed. Instead of modifying existing data structures, functional programming favors creating new ones. Immutability reduces the risk of unexpected side effects and makes programs easier to reason about.
The functions themselves do not operate on the existing data structures, but you could work around it and mutate the data structure by doing something like:
numbers = filter(lambda x: x % 2 == 0, numbers)
This mutates the original data structure. But in the paradigm which is functional programming, this is generally avoided. Instead we create new data structures, promoting clarity, predictability and reasoning about the code.
Lazy Evaluation
- When expressions are not evaluated until their results are actually needed.
- More efficient use of resources, especially when dealing with potentially infinite data structures.
- Promotes modularity and composability.
However, this is not supported by all programming languages.
Foundational Techniques
Recursion
- Functional programming favors recursion over loops.
- Recursion is a natural fit for many functional programming tasks and can lead to more elegant and concise code. Why?
=> Recursion allows functions to express repetitive behavior without relying on mutable state
- This promotes immutability right away. In every function call you are operating on a new structure - local to the context of that call - unlike iteration, where you'd be modifying the same structure over and over.
Function Composition
Function composition is when output of one function is input to another.
Functional programming encourages composing functions together to create new functions. This involves chaining functions together, where the output of one function becomes the input of another. Function composition allows for the creation of complex behaviors from simpler building blocks and promotes code reuse and modularity.
This similar to recursion promotes immutability. Each function operates only on its local context rather than modifying any existing data structure.