Selft training repo
Functional programming treats computation as the evaluation of mathematical functions. It emphasizes immutability, pure functions, and avoiding mutable state and side effects.
Functional programming focuses on expressing computations through the composition and transformation of functions.
Its main focus is on “what to solve” in contrast to an imperative style where the main focus is “how to solve” 1
A pure function always produces the same output for the same input and has no side effects on the program or the environment.
It does not modify any external state or variables and does not rely on any external mutable state.
// Pure function
function add(a, b) {
return a + b;
}
Functional programming encourages the use of immutable data structures, where data cannot be changed after creation.
Instead of modifying data, new data is created with each operation, ensuring data integrity.
Example shows a new array called newArray
being created by copying originalArray
’s elements, and adding up element 4
const originalArray = [1, 2, 3];
const newArray = [...originalArray, 4]; // Creating a new array with 4 added
Functions can take other functions as arguments or return functions as results.
Higher-order functions enable function composition and the creation of abstractions.
Example shows an arrow function multiplying num by 2 is being passed to map
function, which will apply it to each element in numbers array.
const numbers = [1, 2, 3];
const doubledNumbers = numbers.map(num => num * 2);
Functions can be composed by chaining them together, creating more complex behaviors from simpler functions.
The output of one function becomes the input of another.
Example shows a composed function called addAndSquare
, being created by setting add
function to square
function as parameter, the passing it to new function
const add = (a, b) => a + b;
const square = num => num * num;
const addAndSquare = (a, b) => square(add(a, b));
Recursion is a fundamental concept in functional programming where a function calls itself to solve a problem.
It is used instead of iterative loops to achieve repetition.
Example shows recursive factorial
function implementation, where it calls itself each time n-1
until base case n == 0
is reached
function factorial(n) {
if (n === 0) return 1;
return n * factorial(n - 1);
}
Functions are treated as first-class citizens, meaning they can be assigned to variables, passed as arguments, or returned from other functions.
Example shows a function being set to greet
variable, then executed in console.log
, so it´s return will be passed as parameter
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Alice")); // Output: "Hello, Alice!"
A closure is a function that captures and retains the environment (i.e., the variables) in which it was created. This allows the function to access and manipulate the variables from its enclosing scope, even after that scope has finished executing.
Closures are powerful because they enable functions to “remember” their context, making them particularly useful for creating higher-order functions and maintaining state in functional programming.
function createCounter() {
let count = 0;
function increment() {
count++;
console.log("Count:", count);
}
return increment; // Returning the inner function (closure)
}
const counter = createCounter(); // `counter` now holds the closure
counter(); // Output: Count: 1
counter(); // Output: Count: 2
counter(); // Output: Count: 3
In the above example, the function createCounter
creates a closure by defining the inner function increment, which has access to the count
variable from its outer scope.
When you invoke createCounter()
, it returns the inner function increment
, preserving the context (the value of count). The variable count
is part of the closure, and it gets incremented and printed each time you call counter()
, even though createCounter()
has already finished executing.
Here’s an example in JavaScript that incorporates several functional programming principles, including pure functions, immutability, higher-order functions, function composition, and recursion
// Pure Function - Add two numbers
const add = (a, b) => a + b;
// Immutable Data - Using spread operator to create new arrays
const numbers = [1, 2, 3];
const doubledNumbers = [...numbers, 4].map(num => num * 2);
// Higher-Order Function - Filter even numbers from an array
const isEven = num => num % 2 === 0;
const evenNumbers = numbers.filter(isEven);
// Function Composition - Composing functions to get the total sum of even numbers
const sum = arr => arr.reduce((acc, curr) => acc + curr, 0);
const totalSumOfEvenNumbers = sum(evenNumbers);
// Recursion - Calculate the factorial of a number using recursion
const factorial = n => {
if (n === 0) return 1;
return n * factorial(n - 1);
};
// Output the results
console.log("Addition:", add(2, 3)); // Output: 5
console.log("Doubled Numbers:", doubledNumbers); // Output: [2, 4, 6, 8]
console.log("Even Numbers:", evenNumbers); // Output: [2]
console.log("Total Sum of Even Numbers:", totalSumOfEvenNumbers); // Output: 2
console.log("Factorial of 5:", factorial(5)); // Output: 120
A pure functional programming language is a programming language that adheres strictly to the principles of functional programming
Non-pure functional programming languages combine functional programming concepts with other programming paradigms, such as object-oriented programming or imperative programming. These languages provide support for functional programming features but also allow for mutable state, side effects, and imperative constructs.
https://www.baeldung.com/java-functional-programming#:~:text=Basically%2C%20functional%20programming%20is%20a,depends%20only%20on%20its%20input.
https://www.geeksforgeeks.org/functional-programming-paradigm/ ↩