💡 What is Functional Programming?
What is Functional Programming ? It's a programming paradigm that uses Expressions as the building blocks of the code.
In contrast, Imperative Programming is a paradigm that uses Statements to control the flow of the code.
Both programming paradigms have expressions and statements, but in Functional Programming, expressions are primarily used to compose other expressions, while in Imperative Programming, statements are primarily used to control the flow of execution.
fp60-201d.mp4
This insight is widely shared, and many people today may know what Functional Programming is.
However, less well-understood is how functional code achieves control flow without the use of explicit control statements such as if
or for
loops.
The answer is mathematics .
In Functional Programming, code is a composition of expressions, and the evaluation order of these expressions is strictly defined based on mathematical rules.
Therefore, statements that control the flow of code, in principle, are not required in Functional Programming.
fp60-204f.mp4
Let's consider an expression:
This is a binary operation .
Let's consider another expression:
This is a combination of two binary operations.
But, why
In mathematics, the addition operator (+) is left-associative , meaning we perform calculations from left to right when evaluating expressions containing multiple (+) signs .
Parentheses ( ) can override this order. This results in the same answer in both cases, but it highlights the importance of associativity and the use of parentheses to control the order of evaluation .
In contrast, a right-associative operator is an operator that is evaluated from right to left. For example, the exponentiation operator (
It is important to understand the associativity of operators when writing expressions, as it can affect the order of operations and the final result.
It is important to understand the associativity of operators when writing expressions, as it can affect the order of operations and the final result.
In programming language theory, the associativity of an operator is a property that determines how operators of the same precedence are grouped in the absence of parentheses. If an operand is both preceded and followed by operators (for example,
^ 3 ^
), and those operators have equal precedence, then the operand may be used as input to two different operations (i.e. the two operations indicated by the two operators). The choice of which operations to apply the operand to, is determined by the associativity of the operators. Operators may be associative (meaning the operations can be grouped arbitrarily), left-associative (meaning the operations are grouped from the left), right-associative (meaning the operations are grouped from the right) or non-associative (meaning operations cannot be chained, often because the output type is incompatible with the input types). The associativity and precedence of an operator is a part of the definition of the programming language; different programming languages may have different associativity and precedence for the same type of operator.
Expressions follow a set of Operator associativity and precedence rules to determine the order of evaluation.
-
Operator associativity
-
Operator precedence
fp60-205e.mp4
What is Operator precedence ?
Let's consider an expression:
This is a combination of two binary operations.
In this case, it is equivalent to as follows:
In mathematics, operator precedence (or priority ) is a set of rules that define the order in which operations are evaluated in an expression .
It essentially determines which calculations are done first in a math problem with multiple operations .
For example, multiplication is granted a higher precedence than addition, and it has been this way since the introduction of modern algebraic notation.[2][3] Thus, in the expression
$1 + 2 \times 3$ , the multiplication is performed before addition, and the expression has the value$1 + (2 \times 3) = 7$ , and not$(1 + 2) \times 3 = 9$ .
These conventions exist to avoid notational ambiguity while allowing notation to remain brief.[4] Where it is desired to override the precedence conventions, or even simply to emphasize them, parentheses ( ) can be used. For example,
$(2 + 3) \times 4 = 20$ forces addition to precede multiplication, while$(3 + 5)^2 = 64$ forces addition to precede exponentiation.
Knowing how to use operators is essential for Functional Programming because the operator itself knows the order to perform the evaluations .
Accustomed to the conventions of Imperative Programming, many programmers typically employ a for
loop to solve this problem.
let sum = 0;
for (let i = 0; i <= 5; i++) {
sum += i;
}
console.log(sum); // 15
After all, Imperative Programming is an approach to consider the flow of the code.
In computing, iteration is the technique marking out of a block of statements within a computer program for a defined number of repetitions. That block of statements is said to be iterated; a computer scientist might also refer to that block of statements as an "iteration".
Loops constitute the most common language constructs for performing iterations. The following pseudocode "iterates" three times the line of code between begin & end through a for loop, and uses the values of i as increments.
It is permissible, and often necessary, to use values from other parts of the program outside the bracketed block of statements, to perform the desired function.
Iterators constitute alternative language constructs to loops, which ensure consistent iterations over specific data structures. They can eventually save time and effort in later coding attempts. In particular, an iterator allows one to repeat the same kind of operation at each node of such a data structure, often in some pre-defined order.
Iteratees are purely functional language constructs, which accept or reject data during the iterations.
On the other hand, Functional Programming is an approach to consider the expressions of Mathematics.
The most important thing to understand is that this is a straightforward calculation .
However, in Imperative Programming, solving such problems often involves designing loops with ever-changing variables . This can lead to complexities that obscure the problem's essence and increase the risk of introducing bugs.
The calculation for this problem proceeds as follows.
So, know the operators in Functional Programming.
The adequate operator that is capable of calculating this structure is called Fold (higher-order function)
In Functional Programming, fold (also termed reduce, accumulate, aggregate, compress, or inject) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results of recursively processing its constituent parts, building up a return value.
let plus = (a,b) => a + b;
let sum = [0,1,2,3,4,5].reduce(plus);
console.log(sum); // 15
let sum = [0;1;2;3;4;5] |> List.reduce (+)
printfn "%d" sum // 15
let reducer = List.reduce (+)
let sum = [0;1;2;3;4;5] |> reducer
printfn "%d" sum // 15
Here, we simply prepare the set of integers from 0 to 5 as [0,1,2,3,4,5]
or [0;1;2;3;4;5]
, but it's also possible to calculate such a list/array by using an adequate operator/function.
Conceptually, it's known as Unfold.
// Generate [0;1;2;3;4;5] using unfold
let numbers = unfold (0, fun x -> x + 1) 6
In Functional Programming, typically, every element is either an expression or an operation. This paradigm allows for computations to be performed without the need for traditional control flow structures such as for
loops.
In Imperative Programming languages, the term "conditional statement" is usually used, whereas in Functional Programming, the terms "conditional expression" or "conditional construct" are preferred, because these terms all have distinct meanings.
In modern programming languages, if/else
is no longer a statement but an expression and operators :
Rust takes advantage of if/else
expression.
Older languages like JavaScript have if/else
statements, but JavaScript also includes a Conditional (ternary) operator.
The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (
?
), then an expression to execute if the condition is truthy followed by a colon (:
), and finally the expression to execute if the condition is falsy. This operator is frequently used as an alternative to anif...else
statement.
let age = 26;
let beverage =
age >= 21
? "Beer"
: "Juice";
console.log(beverage); // "Beer"
F# takes advantage of if/else
expression.
Conditional Expressions: if...then...else
let age = 26
let beverage =
if age >= 21
then "Beer"
else "Juice"
printfn "%s" beverage // "Beer"
How about more complicated conditional pattern?
JavaScript: switch-case statements
F#: Pattern matching expressions
let x = 2
let result =
match x with
| 1 -> "one"
| 2 -> "two"
| _ -> "other"
// "two"
In Imperative Programming, the order of statements in the code, from top to bottom , determines the sequence of their execution without the need for explicit control structures .
console.log "Hello";
console.log "world!";
This style of Imperative Programming is also used in functional languages like F# due to its simplicity, which is good.
printfn "Hello"
printfn "world!"
On the other hand, pure functional languages like Haskell do not allow this style for sequnece simply because there is a strict rule that the order of code execution is determined by the order of mathematical operations.
However, the use of do
notation allows programmers to write code in a similar style of Imperative Programming.
main = do
putStrLn "Hello"
putStrLn "world!"
do
notation is a syntactic sugar in Haskell that offers a more concise and expressive way to write sequences of operations. The corresponding unadorned expression is as shown below:
main = putStrLn "Hello" >> putStrLn "world!"
This is called IO monad , and conceptually, not too far code in JavaScript would be:
let main =
() => [undefined]
.map(() => console.log("Hello"))
.map(() => console.log("world!"));
main();