10 coding learning exercises focused on Functional Programming in JavaScript, covering Pure Functions and Immutability, Map, Filter, Reduce, and other Array Methods, Declarative vs. Imperative Programming, Lazy Evaluation and Infinite Sequences, and Monads, Functors, and Other FP Concepts
Exercise 1: Creating Pure Functions
Objective: Understand the concept of pure functions in JavaScript.
Task: Write a pure function multiply
that takes two numbers as arguments and returns their product. Ensure the function does not have any side effects.
function multiply(a, b) {
return a * b;
}
console.log(multiply(2, 3)); // Outputs: 6
console.log(multiply(2, 3)); // Outputs: 6
Explanation: The multiply
function is pure because it always returns the same result given the same inputs and does not modify any external state or variables.
Exercise 2: Implementing Immutability
Objective: Practice working with immutable data structures.
Task: Create a function updateName
that takes an object and a new name as arguments and returns a new object with the updated name, without modifying the original object.
const person = { name: "Alice", age: 25 };
function updateName(person, newName) {
return { ...person, name: newName };
}
const updatedPerson = updateName(person, "Bob");
console.log(person); // Outputs: { name: "Alice", age: 25 }
console.log(updatedPerson); // Outputs: { name: "Bob", age: 25 }
Explanation: The updateName
function creates a new object with the updated name while leaving the original person
object unchanged, demonstrating immutability.
Exercise 3: Using the map
Method
Objective: Learn how to transform arrays using the map
method.
Task: Create an array of numbers and use the map
method to create a new array where each number is squared.
const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(n => n * n);
console.log(squaredNumbers); // Outputs: [1, 4, 9, 16, 25]
Explanation: The map
method is used to create a new array by applying a function (squaring each number) to each element of the original array.
Exercise 4: Filtering Arrays with filter
Objective: Practice filtering arrays based on a condition.
Task: Create an array of numbers and use the filter
method to create a new array containing only the even numbers.
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter(n => n % 2 === 0);
console.log(evenNumbers); // Outputs: [2, 4, 6]
Explanation: The filter
method is used to create a new array containing only the elements that satisfy the condition (being even numbers).
Exercise 5: Reducing Arrays with reduce
Objective: Understand how to reduce arrays to a single value using the reduce
method.
Task: Create an array of numbers and use the reduce
method to calculate the sum of all elements in the array.
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // Outputs: 15
Explanation: The reduce
method is used to accumulate the sum of all numbers in the array by iterating through each element and adding it to the accumulator.
Exercise 6: Declarative vs. Imperative Programming
Objective: Compare declarative and imperative programming styles.
Task: Write two functions, one using an imperative style and one using a declarative style, to double each number in an array.
Imperative Approach:
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = [];
for (let i = 0; i < numbers.length; i++) {
doubledNumbers.push(numbers[i] * 2);
}
console.log(doubledNumbers); // Outputs: [2, 4, 6, 8, 10]
Declarative Approach:
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(n => n * 2);
console.log(doubledNumbers); // Outputs: [2, 4, 6, 8, 10]
Explanation: The imperative approach explicitly details each step to double the numbers, while the declarative approach uses the map
method to express the same logic in a more concise and readable way.
Exercise 7: Implementing Lazy Evaluation with Generators
Objective: Learn how to implement lazy evaluation using generators.
Task: Create a generator function infiniteNumbers
that generates an infinite sequence of numbers starting from 0. Use the generator to retrieve the first 5 numbers.
function* infiniteNumbers() {
let i = 0;
while (true) {
yield i++;
}
}
const numbers = infiniteNumbers();
console.log(numbers.next().value); // Outputs: 0
console.log(numbers.next().value); // Outputs: 1
console.log(numbers.next().value); // Outputs: 2
console.log(numbers.next().value); // Outputs: 3
console.log(numbers.next().value); // Outputs: 4
Explanation: The infiniteNumbers
generator function produces numbers indefinitely but only computes the next number when next
is called, demonstrating lazy evaluation.
Exercise 8: Working with Infinite Sequences
Objective: Practice extracting finite values from an infinite sequence.
Task: Create a function take
that extracts the first n
values from a generator and returns them in an array.
function* infiniteNumbers() {
let i = 0;
while (true) {
yield i++;
}
}
function take(generator, n) {
const result = [];
for (let i = 0; i < n; i++) {
result.push(generator.next().value);
}
return result;
}
const firstFive = take(infiniteNumbers(), 5);
console.log(firstFive); // Outputs: [0, 1, 2, 3, 4]
Explanation: The take
function interacts with the infinite sequence generator, extracting the first n
values and returning them in an array.
Exercise 9: Understanding Functors with map
Objective: Explore the concept of functors using JavaScript arrays.
Task: Use the map
method on an array of strings to convert each string to uppercase, demonstrating that arrays are functors.
const words = ["hello", "world", "javascript"];
const uppercaseWords = words.map(word => word.toUpperCase());
console.log(uppercaseWords); // Outputs: ["HELLO", "WORLD", "JAVASCRIPT"]
Explanation: Arrays in JavaScript are functors because they implement the map
method, allowing the application of a function to each element and returning a new array with the results.
Exercise 10: Exploring Monads with Promises
Objective: Learn about monads through the chaining capabilities of JavaScript Promises.
Task: Create a promise that resolves with a number, then chain multiple .then
calls to multiply the number by 2, add 3, and log the result.
const fetchNumber = () => Promise.resolve(5);
fetchNumber()
.then(num => num * 2)
.then(num => num + 3)
.then(result => console.log(result)); // Outputs: 13
Explanation: Promises in JavaScript can be considered monads because they allow chaining of operations using .then
, enabling a sequence of transformations to be applied to the resolved value.