Skip to content

Module 4 : Functional Programming

Rishabh Malviya edited this page Feb 24, 2023 · 4 revisions

Functional Programming

Functional programming falls under the umbrella of declarative programming paradigms: it’s a paradigm that expresses a set of operations without revealing how they’re implemented or how data flows through them.

The more popular models today are imperative or procedural and are supported in most structured and object-oriented languages like Java, C#, C++, and others. Imperative programming treats a computer program as merely a sequence of top-to-bottom statements that changes the state of the system in order to compute a result.

In the context of JavaScript Functional Programming is well implementable because in JavaScript functions are first-class citizens. This means that we can work with functions in JavaScript in the same way as variables.

The Goal of Functional Programming is to abstract control flows and operations on data with functions in order to avoid side effects and reduce mutation of state in your application.

Example:

// Imperative Programming
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for(let i = 0; i < array.length; i++) {
 array[i] = Math.pow(array[i], 2);
}
console.log(array); //-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

//Declarative Programming
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(
function(num) {
return Math.pow(num, 2);
}); 
//-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Pure Function

A pure function is a function that has the following qualities :

  1. It depends only on the input provided and not on any hidden or external state that may change during its evaluation or between calls.

  2. It doesn’t inflict changes beyond its scope, such as modifying a global object or a parameter passed by reference. (Side Effects).

  3. It will always give the same result on the same set of arguments passed to it.

// Impure Function
const a = 10;
function add(b){
    console.log(a+b);   
}
add(3);


const count = 5;
function counter(){
    return count++;
}
count();

// Pure Function
// 13
function add(a,b){
    console.log(a+b);
}
add(7,6);
// 13 

The function add(b) is dependent on the global variable a.

The function counter() is creating a side effect by modifying a global variable count.

Immutability

Immutable data is data that can’t be changed after it’s been created. In JavaScript, all primitive types (String, Number, and so on) are inherently immutable. But other objects, like arrays, aren’t immutable, even if they’re passed as input to a function.

Example

const Bike = {
  model: 'Ninja',
  year: 2020
  };

const newBike = car;
  newBike.model = 'Royal Enfield';

There are few libraries like Immutable.js available for brining immutability in JavaScript. We can also rely on the on vanilla JavaScript.

const Bike = {
  model: 'Ninja',
  year: 2020
  };

const newBike = Object.assign({}, bike, {
  model: 'Royal Enfield'
  })
var countdown = function(value) {
    if (value > 0) {
        console.log(value);
        return countdown(value - 1);
    } else {
        return value;
    }
};
countdown(10);

Referential Transparency

Referential transparency is a more formal way of defining a pure function. Purity in this sense refers to the existence of a pure mapping between a function’s arguments and its return value. Hence, if a function consistently yields the same result on the same input, it’s said to be referentially transparent.

Example

var counter = 0;
function increment(){
    return ++counter;
}

var increment = counter => counter + 1;
  • The first increment function is dependent on the external variable counter.

  • After removing its dependent state the outer variable and making it an explicit formal parameter of the function signature.

  • Now the second increment is totally independent of the external variable and will create generate the same output with the same arguments.

Recursion

What is Recursion?

 Recursion is a technique designed to solve problems by decomposing them into smaller, self-similar problems that, when combined, arrive at the original solution. A recursive function has two main parts:

  1. Base cases (also known as the terminating condition)

  2. Recursive cases   

Functional Programming promotes the usage of recursion instead of loops.

// using loops
var counter = 10;
while(counter > 0) {
    console.log(counter--);
}
// using recursion
var countdown = function(value) {
    if (value > 0) {
        console.log(value);
        return countdown(value - 1);
    } else {
        return value;
    }
};
countdown(10);

First-Class and High-Order Function

First Class functions are the functions we are treated like first-class citizens like variables. We can store them in a variable and pass them to other functions like the callback function.

In JavaScript, we have options with functions. It can be :

  1. Assigned to variables as an anonymous function or lambda expression        
var square = x => x * x;
  1. Assigned to object properties as methods:
var obj = {
    method : function (x) {return x * x}
};
  1. Functions can return another function
function add(a,b){
    return function(){
         return a+b;
    }
}
  1. Functions can be passed as arguments to other functions
function hello(greet,name){
    console.log(greet(),name);
}
var greet = () => console.log("Hello");

Strict versus non-strict Evaluation

Functional languages can be categorized by whether they use strict (eager) or non-strict (lazy) evaluation, concepts that refer to how function arguments are processed when an expression is being evaluated. The technical difference is in the denotational semantics" of expressions containing failing or divergent computations. Under strict evaluation, the evaluation of any term containing a failing sub-term fails. For example, the expression:

Example

console.log([4-2, 1/0, 3*2, 1+9].length);
// 4
arr :=int[]{4-2, 1/0, 3*2, 1+9};
fmt.Println(len(arr));
// invalid opeartion division by 0

Arrays High Order Functions

map

The map() method is an iterative method. It calls a provided callbackFn function once for each element in an array and constructs a new array from the results.

const arr = [1,2,3,4,5];
const doubleArr = arr.map(val => val*2);
console.log(doubleArr);
// 2,4,6,8,10

filter

The filter() method is an iterative method. It calls a provided callbackFn function once for each element in an array, and constructs a new array of all the values for which callbackFn returns a truthy value. Array elements which do not pass the callbackFn test are not included in the new array.

const array = [, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
const oddArr = array.filter(val => val % 2 == 1);
console.log(oddArr);
// 5, 7, 9, 11, 13

reduce

The reduce() method is an iterative method. It runs a "reducer" callback function over all elements in the array, in ascending-index order, and accumulates them into a single value. Every time, the return value of callbackFn is passed into callbackFn again on next invocation as accumulator. The final value of accumulator (which is the value returned from callbackFn on the final iteration of the array) becomes the return value of reduce().

const array = [, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
const arraySum = array.reduce((acc,val) => acc+val);
console.log(arraySum)
// 85

Arity

Arity (from Latin) is the term used to refer to the number of arguments or operands in a function or operation, respectively. You’re most likely to come across this word in the realm of JavaScript when it’s used to mention the number of arguments expected by a JavaScript function.

function getName(first, middle, last) {
  return first + ' ' + middle + ' ' + last;
}

The arity of the above function getName is 3

Curried Functions

Currying is the transformation of a function with multiple arguments into a sequence of single-argument functions. This is used in JavaScript quite often to reduce dependency of arguments on function by divide the logic into multiple-inner function with arguments.

function curry(f) { // curry(f) does the currying transform
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let curriedSum = curry(sum);

alert( curriedSum(1)(2) ); // 3

Functors

An object we can map and apply a function in order to generate another object of the same type.

var arr = [2,6,7,8,9,11];
var multi = x => x*2;
var multArr = arr.map(val => mutli(val));

Closures

closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).

In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Lexical Scoping

function init() {
  var name = "Mozilla"; 
  function displayName() {    
    console.log(name); 
  }
  displayName();
}
init();

The inner function dispalyName has access of thename variable.

Clone this wiki locally