TESTING AREA for teachers - real service at plus.tuni.fi

Functional JavaScript

What is functional programming?

Functional Programming (FP) is a programming paradigm that models computation as the evaluation of mathematical functions. It avoids changing state and mutable data. At its core, FP relies on functions as the fundamental building blocks of your code, rather than imperative sequences of commands.

Functions are essential to both functional programming and algorithms. In algorithm design, one often decomposes complex tasks into smaller, reusable functions. Alongside functions, data structures,the way data is organized and stored,play a vital role. FP typically handles data differently by enforcing immutability: once data is created, it doesn’t change. If modifications are needed, a new copy of the data is produced.

This makes programs more predictable and easier to debug.

The connection to math

Functions are central to mathematics as well. This concept carries over to computing, where computational thinking builds on mathematical foundations. The transition from solving mathematical problems to solving computational ones is natural and often seamless.

See the figure “The learning trajectory of CT” for more:

Functional programming in JavaScript

There exist some “pure” FP languages like Haskell and Elm. JavaScript is not pure FP language but it can be used like one. We can write JavaScript code that follows the principles of functional programming, and we can use libraries that help us to write functional code. By now, you have learned a little about JavaScript functions. Next, we will go through JavaScript code samples that concentrate on these functional features in particular.

Key characteristics of functional programming

1. Immutable data: You do not ever change a value of a variable. Let’s say that you have a chair object. This chair defines a property of its current location. If you want to move the chair object, you create a copy of the old chair and replace the old location with a new one. Sounds like a lot of work, but it makes tracking bugs way simpler, therefore the total amount of work may decrease.

Tips:


2. Pure functions:

  • cause no side-effects such as console.log(), API calls, or global data modifications,

  • return always the same output for the same input

In which situations can a function return a different value for the same parameters?

For example, if some conditions depend on global variables that can be muted in an outer scope.

If you are using Math.random

Although pure sounds better than impure, impurity is necessary for interacting with an impure world, for example, with a user. You cannot build a functional web site with only pure functions. If you were to use only pure functions you could not:

manipulate the DOM,

fetch data from a server, or

send data to a server.

Thus, both function types play an important role in web development. Yet separating functionality to pure business logic and impure functionality (data fetching, DOM manipulation etc.) might be a good idea. This way you could ensure that your business logic is easily testable and re-factorable. Business logic mixed with side-effects make modifying applications much more demanding.

Tips:

  • Once a function is tested, remove console.log debug prints

  • Do not directly mutate data passed to your function. Create a copy, modify it and return the modified copy.

  • Do not access any values outside of your function

  • Always return what was modified inside function

  • Separate side-effects to their own functions


3. Higher-order functions: Functions in JavaScript are objects. What this means in practice is that you can assign a function to a variable, to array elements, or to an object. In addition, as an object, function has its own prototype property with a value Function.prototype. Function.prototype defines three functions: call(), bind(), apply().

const greet = (name) => console.log(`Greetings, ${name}`);
greet("Snow White"); // Greetings, Snow White

In the example above, we pass a string as a parameter to the function greet. Instead of a string, we could also pass a function as a parameter.

const sayHi = () => console.log("Hi!");
const sayHello = () => console.log("Hello");
const greet = (type, sayHi, sayHello) => type === "Hi" ? sayHi() : sayHello();

greet("Hi", sayHi, sayHello); // Hi!
greet("Hello", sayHi, sayHello); // Hello

[The syntax ? : used in the previous example is called ternary operator. It’s basically a shorter version of if-else.]

Here, we could say that the function greet() is a higher-order function, because it takes a function as a parameter. Passing functions as parameters implies also that functions are first-class citizens in the language.

Attention

When passing a function as a parameter, you are not calling the function and therefore you omit the parentheses (and possible arguments) and only pass the name of the function. Calling the function actually returns what the function returns and then passes that return value to the higher-order function.

const sayHi = () => console.log("Hi!");
const sayHello = () => console.log("Hello");
const greet = (type, sayHi, sayHello) => type === "Hi" ? sayHi() : sayHello();

greet("Hi", sayHi, sayHello); // pass in functions
greet("Hello", sayHi(), sayHello()); // pass in results of the functions "Hi!" and "Hello"

4. Function composition: Function composition is used in combining multiple simple functions into a more complex one. Function composition is an example of using higher-order functions. The result of each function is passed as a parameter to the next one. We define two functions: the first function adds 2 to the parameter, and the other one multiplies by it 2.

const add2        = n => n + 2;
const times2      = n => n * 2;

Now these functions can be used and re-used in multiple places and one could even apply the functions in different order or multiple times to produce different end results.

console.log(add2(times2(5))); // 12
console.log(times2(add2(5))); // 14
console.log(add2(times2(add2(5)))); // 16
console.log(times2(add2(times2(5)))); // 24

What if we had to write something like this?

const accessRights = a(b(c(d(e(f(g("Snow White")))))));

Pure functions enable function compositions to be as long as we wish, provided that output of the previous function is the anticipated input of the next function in a chain.

Tips:

  • In real scenario you would rather a separate compose function by using libraries like Lodash.

Exercises

In the following short exercises, the goal is to differentiate pure functions from impure.

Consider the following code sample:

let numbers = [1, 2, 3, 4];

function addFive(numberArray) {
  console.log(numberArray);
  numberArray.push(5);
  console.log(numberArray);
}

addFive(numbers);

Which characteristics of impure functions can you identify?

Consider the following code sample:

function printEmployees() {

  const url = "http://dummy.restapiexample.com/api/v1/employees";

  fetch(url)
    .then((response) => {
      return response.json();
    })
    .then((myJson) => {
      console.log(myJson.data);
    });
}

What characteristics of impure function are present?

Consider the following code sample:

const numbers = [1, 2, 3, 4];

const addFive = (numbers) => [...numbers, 5]

addFive(numbers);

What characteristics of impure function are present?

Consider the following code sample:

const render = (elem, text) => document.getElementById(elem).textContent = text;

render("header", "Hello World!");

What characteristics of impure function are present?

Consider the following code sample:

const numbers = [1, 2, 3, 4];

function addDate(numberArray) {
  const date = new Date(Date.now());
  return [...numberArray, date.getDate()];
};

const modified = addDate(numbers);

What characteristics of impure function are present?

We can represent a list of numbers [2, 4, 1, 3, 0] visually as follows (red numbers being indices; the task inspired by ©Bebras(2022-AT-04)) :

X 02 14 21 33 40

We write (X 1) to refer to the number at index position 1 in the list X, so:

  • (X 1) is 4

  • (X 4) is 0

Positions can also be indicated indirectly. For example:

  • (X (X 2)) is 4 because:

    • (X 2) is 1, so

    • (X (X 2)) = (X 1) = 4

Here are three lists: A, B, and C:

A 0🍑 1🍋‍🟩 2🍐 3🍇 4🍉
B 04 13 21 32 40
C 02 14 21 33 40
Posting submission...