Tricky Concepts of Functional JavaScript

Fasae Oluwakemi
Devcrib
Published in
6 min readDec 12, 2017

Functional JavaScript has the advantage of tighter codes that get right to the point in the fewest lines possible, and with less repetition. Sometimes this can come at the expense of readability until one is familiar with the way the functional programming works. Codes written this way can be harder too, to read and understand.

Currying and partial application

In JavaScript, a function can be transformed into another function with the use of currying and partial application. Although these two are similar and are often mistaken for each other, in that they both transform a function into another, they work differently. Let’s take a peek into how they work.

Currying

Currying is an incredibly useful technique in functional JavaScript that allows the programmer generate a library of small easily configured functions that are readable, quick to use and behave consistently. Basically, it is the technique of translating/transforming/changing a single function that has n arguments to ‘n functions’ with a single argument each. Although currying isn’t built-in, it can be put to work once it is understood. Its advantages are explored when you need to create a lot of very detailed custom functions. The only problem it poses to the programmer is the syntax. As you build these curried functions up, you need to keep nesting return functions, and call them with new functions that require multiple set of parentheses, each containing its own isolated argument. This can become messy.

One thing that’s important to keep in mind when currying is the order of the arguments. Using the approach that has been described, you obviously want the argument that you’re most likely to replace from one variation to the next to be the last argument passed to the original function.

In summary, currying is a way of constructing functions that allow partial application (which you would find discussed as you keep reading) of a function’s arguments. What this means is that you can pass all of the arguments and get a function back that’s waiting for the rest of arguments. It’s really that simple!

The algorithm of currying is:

curFunction: (X * Y -> R) --> (X -> (Y -> R))

curFunction takes a binary function and returns a unary function that returns a unary function. The code below show this.

function curFunction(f){
return function (x){
return function(y){
return f(x, y);
}
}
}

Partial Application

Partial application can be described as taking a function that only accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining unbound arguments. This means that given any arbitrary function, a new function can be generated that has one or more arguments bound or partially applied.

Partial application takes a function:

f: X * Y -> R

and a fixed value of x for the first argument to produce a new function

f: Y -> R

The first f does the same thing as the second f but only has to fill in the second parameter which is why the number of arguments it has is less. One says the first is bound to x.

Examples are implemented below. You can try them out here — https://repl.it/@king_tomiiide/currying-VS-partial-application

//Currying
function map(array) {
let newArray = [];
return function(func) {
for(i = 0 ; i<array.length; i++){
newArray[i] = func(array[i]);
}
return newArray;
}
}

function double(value){
return 2 * value;
}

map([2,4])(double);


//Partial Application
function square(array){
let newArray = []
for(i = 0; i < array.length; i++){
newArray[i] = array[i] * array[i];
}
return newArray;
}

let arr = [2,5];

square(square(map(arr)(double)));

Differences between Currying and Partial Application

Although the two work almost alike by transforming a function with a similar arity, they are different and shouldn’t be used interchangeably. Currying always produces nested unary (1-ary) functions which are still largely the same as the original while partial application produces functions of arbitrary arity. The transformed function of a partial application is different from the original in that it needs less arguments.

Composition and Pipelining

A pipeline is an extensible software framework that defines and links together one or more stages of a business process, running them in sequence to complete a specific task. This also applies to programming. A pipeline is multiple processes that are chained together. Each process takes an input from the previous process’s output, and then passes it’s output along to the next process. It is very similar to how thecompose function works. The only difference is that pipeline evaluates from left-to-right instead. It processes the outer function and passes the result to the inner function as parameter.

Composition on the other hand works like pipeline which take an input from the previous process and passes its output along to the next process. It only evaluates from right to left. It evaluates the inner function and passes the result to the outer function which it uses for processing.

Examples Implemented

//function definitions
const compose = (...fns) => {
return (initialVal) => {
return fns.reduceRight((val, fn) => {
return fn(val);
}, initialVal);
}
}

const pipe = (...fns) => {
return (initialVal) => {
return fns.reduce((val, fn) => {
return fn(val);
}, initialVal);
}
}

function map(array) {
let newArray = [];
return function(func) {
for(i = 0 ; i<array.length; i++){
newArray[i] = func(array[i]);
}
return newArray;
}
}

function filter(array){
let newArray = [];
return function(func){
for(i = 0; i < array.length; i++){
if(func(array[i])) newArray.push(array[i]);
}
return newArray;
}

}

function doubleArray(array){
function double(value){
return 2 * value;
}
return map(array)(double);
}

function removeLessthan10Array(array){
return filter(array)( (value) => value < 10 );
}

//composition
filterAndDouble = compose(doubleArray,removeLessthan10Array);

//Piping
doubleAndFilter = pipe(doubleArray,removeLessthan10Array);


console.log(doubleAndFilter([1,4,5,6]));
console.log(filterAndDouble([1,4,5,6]));

Functors and Monads

Functor is a function that, given a value and a function, unwraps the values to get to its inner value(s), calls the given function with the inner value(s), wraps the returned values in a new structure, and returns the new structure. Functors are the containers that can be used with ‘map’ function. I t can be mapped upon by a Unary function. Mapped upon here means that the container can be treated with a special function which applies any unary function to every content of the container and returns a container with the resulting values as its contents. Example of functors are Data.array, Data.Maybe, Data.Either.

Monad is a design pattern used to describe computations as a series of steps. They are extensively used in pure functional programming languages to manage side effects but can also be used in multiparadigm languages to control complexity. It is a way of composing functions that require context in addition to the return value such as computation, branching, or I/O.

An example is implemented below to show the difference between functors and monads

let capitalize = (name) => name.toUpperCase();

//functors

const names = ['dotun','shane','segun','wale'];
let capital = names.map( capitalize );
console.log(capital);


//monads
function greetNames(names) {
return function cap(func){
var result = 'Welcome ';
names.forEach( function (name) {
result += func(name) + ', ';
});
return result;
}

}

console.log(greetNames(names)(capitalize));

Three Components of Monad

The three components of a monad are: i.) Type constructor ii.) Unit function iii.) Maybe monad.

The type constructor is a feature that creates a monadic type for the underlying type.

The unit function wraps a value of an underlying type in a monad

The maybe monad wraps the value 2 of the type number into the value Maybe(2) of the type Maybe <number>.

Differences

A functor is a function from a set of values a to another set of values: a -> b, e.g a function that goes from string to an integer.

Monads allow to compose functors that are not composable. They can compose functors by adding extra functionality in the composition.

In conclusion, every monad is not a functor but uses a functor to complete its purpose.

References

--

--