Published on

Functional Programming

Authors
intro

What's that for?

Functional Programming is a programming paradigm like object-oriented programming and procedural programming.

Instead of objects and classes, Functional Programming uses functions and closures.

Input-and-Output(I/O) is one of the key factors in Functional Programming such as Functions. It establishes a strong association between all the other concepts explained in the following sections.

One must consider the following concepts to implement Functional Programming:

  • Pure Functions
  • Higher-Order Functions
  • Closure
  • Curry
  • Composition

Pure Functions

A pure function is a type of function that holds the following properties:

  • Always return the same output for the same input.
  • no relation or reference to anything outside of the function.
  • Avoid stateful data and loops; use recursion instead.

Now you may complain about how tiresome to write pure functions. However, there are some techniques to implement pure functions.

Minimize Side Effects

Side effects are any change of state or value outside of the function.

That is, side effects are profoundly hostile to pure functions as you might lose the flow of control in your computer program and not get the desired output.

Well, here is what to do:

  • Remove unused references
var x = f(4) // x is not used, so remove this line
var y = g(x)

console.log(y)

Memoization

Memoization is a technique to cache the results of a function.

Thus we can store the state of the function at some point and use it later.

function add(a, b) {
  return a + b
}

// Memoize - Cache
var myAdd = memoize(add)

myAdd(2, 3) // 5, add() is called
myAdd(1, 3) // 4, add() is called
myAdd(2, 3) // 5, add() is not called

Higher-Order Functions

Higher-order functions take other functions as arguments or return a function as a result.

Benefits

  • Reusable functions
  • Simple code
  • Reduce code duplication

Example

Let's say we want to add one to each element of an array and print the output array.

  • Without higher-order functions
const numbers = [1, 2, 3, 4, 5]

function increment(array) {
  for (let i = 0; i < array.length; i++) {
    console.log(array[i] + 1)
  }
}

increment(numbers)
  • With higher-order functions
const numbers = [1, 2, 3, 4, 5]

numbers.forEach((number) => console.log(number + 1)) // Returns a function

Closure

A closure is a record that stores a function together with an environment. Thus a closure gives you access to an outer function's scope from an inner function's scope even after the code executes.

Closures aim portable functions.

Example

  • Outer scope
var a = 1
function()
{
  console.log(a); // works
}

console.log(a); // works
  • Local scope
function()
{
  var a = 1
  console.log(a); // works
}

console.log(a); // fails
  • How closure allows a function to access to its outer scope.
function outerPrint() {
  var a = 1 // outer scope variable
  function innerPrint() {
    console.log(a) // works
  }

  innerPrint() // works
}

Currying

Currying breaks down a function of multiple arguments into a function of single arguments.

Example

let x = f(a, b, c)

// Currying
let h = g(a)
let i = h(b)
let x = i(c)

// or simply
let x = g(a)(b)(c)

Composition

While implementing your business logic, you will often use the output of a function as input for another function.

That might cause critical problems in the state of the program. Therefore, we can use composition(compose(f,g,h)(obj)) to combine functions instead of chaining functions(obj.f().g().h()).

Example

;[1, 2, 3, 4, 5, 6, 7, 8, 9]
  .filter((x) => x % 2 === 0) //  [2,4,6,...,18]
  .map((x) => x * 2) // [6,12,18]
  .reduce((x, y) => x + y) // 36

Benefits of Functional Programming

  • Easy to test
  • More simple & less buggy
  • Deterministic, allowing parallel programming
  • Thread-safe functions
  • Easy to handle events and errors

References