DEV Community

Muhammad Ahmad
Muhammad Ahmad

Posted on • Updated on

Functional Programming in JS: 0x05 - Implementing Techniques - Theme 1

Hold on to your hats because we’re really going to get into the functional mind-set now.
we’re going to do the following:

  • Put all the core concepts together into a cohesive paradigm
  • Explore the beauty that functional programming has to offer when we fully commit to the style
  • Step through the logical progression of functional patterns as they build upon each other
  • All the while, we will build up a simple application that does some pretty cool stuff

Functional programming can come in a variety of flavors and patterns. This part will cover many different styles of functional programming:

  • Data generic programming
  • Mostly functional programming
  • Functional reactive programming and more

This part, however, will be as style-unbiased as possible. Without leaning too hard on one style of functional programming over another, the overall goal is to show that there are better ways to write code than what is often accepted as the correct and only way.

Once you free your mind about the preconceptions of what is the right way and what is not the right way to write code, you can do whatever you want. When you just write code with childlike abandon for no reason other than the fact that you like it and when you’re not concerned about conforming to the traditional way of doing things, then the possibilities are endless.

Partial function application and currying

Many languages support optional arguments, but not in JavaScript. JavaScript uses a different pattern entirely that allows for any number of arguments to be passed to a function. This leaves the door open for some very interesting and unusual design patterns. Functions can be applied in part or in whole.

Partial application in JavaScript is the process of binding values to one or more arguments of a function that returns another function that accepts the remaining, unbound arguments.
Similarly, currying is the process of transforming a function with many arguments into a function with one argument that returns another function that takes more arguments as needed. The difference between the two may not be clear now, but it will be obvious in the end.

Function manipulation

Actually, before we go any further and explain just how to implement partial application and currying, we need a review. If we’re going to tear JavaScript’s thick veneer of C-like syntax right off and expose it’s functional underbelly, then we’re going to need to understand how primitives, functions, and prototypes in JavaScript work; we would never need to consider these if we just wanted to set some cookies or validate some form fields.

Apply, call, and the this keyword

In pure functional languages, functions are not invoked; they’re applied. JavaScript works the same way and even provides utilities for manually calling and applying functions. And it’s all about the this keyword, which, of course, is the object that the function is a member of.
The call() function lets you define the this keyword as the first argument. It works as follows:

console.log(['Hello', 'world'].join(' ')) // normal way
console.log(Array.prototype.join.call(['Hello', 'world'], ' ')); // using call
Enter fullscreen mode Exit fullscreen mode

The call() function can be used, for example, to invoke anonymous functions:

console.log((function() {
    console.log(this.length)
}).call([1, 2, 3]));
Enter fullscreen mode Exit fullscreen mode

The apply() function is very similar to the call() function, but a little more useful:

console.log(Math.max(1, 2, 3)); // returns 3
console.log(Math.max([1, 2, 3])); // won't work for arrays though
console.log(Math.max.apply(null, [1, 2, 3])); // but this will work
Enter fullscreen mode Exit fullscreen mode

The fundamental difference is that, while the call() function accepts a list of arguments, the apply() function accepts an array of arguments.
The call() and apply() functions allow you to write a function once and then inherit it in other objects without writing the function over again. And they are both members themselves of the Function argument.

Note

This is bonus material, but when you use the call() function on itself, some really cool things can happen:

// these two lines are equivalent
func.call(thisValue);
Function.prototype.call.call(func, thisValue);
Enter fullscreen mode Exit fullscreen mode

Binding arguments

The bind() function allows you to apply a method to one object with the this keyword assigned to another. Internally, it’s the same as the call() function, but it’s chained to the method and returns a new bounded function.
It’s especially useful for callbacks, as shown in the following code snippet:

function Drum() {
    this.noise = 'boom';
    this.duration = 1000;
    this.goBoom = function() {
        console.log(this.noise)
    };
}
var drum = new Drum();
setInterval(drum.goBoom.bind(drum), drum.duration);
Enter fullscreen mode Exit fullscreen mode

This solves a lot of problems in object-oriented frameworks, such as Dojo, specifically the problems of maintaining the state when using classes that define their own handler functions. But we can use the bind() function for functional programming too.

Tip

The bind() function actually does partial application on its own, though in a very limited way.

Function factories

Remember our section on closures in FP in JS: 0x03? Closures are the constructs that makes it possible to create a useful JavaScript programming pattern known as function factories. They allow us to manually bind arguments to functions.
First, we’ll need a function that binds an argument to another function:

function bindFirstArg(func, a) {
    return function(b) {
        return func(a, b);
    };
}
Enter fullscreen mode Exit fullscreen mode

Then we can use this to create more generic functions:

var powersOfTwo = bindFirstArg(Math.pow, 2);
console.log(powersOfTwo(3)); // 8
console.log(powersOfTwo(5)); // 32
Enter fullscreen mode Exit fullscreen mode

And it can work on the other argument too:

function bindSecondArg(func, b) {
    return function(a) {
        return func(a, b);
    };
}
var squareOf = bindSecondArg(Math.pow, 2);
var cubeOf = bindSecondArg(Math.pow, 3);
console.log(squareOf(3)); // 9
console.log(squareOf(4)); // 16
console.log(cubeOf(3)); // 27
console.log(cubeOf(4)); // 64
Enter fullscreen mode Exit fullscreen mode

The ability to create generic functions is very important in functional programming. But there’s a clever trick to making this process even more generalized. The bindFirstArg() function itself takes two arguments, the first being a function. If we pass the bindFirstArg function as a function to itself, we can create bindable functions. This can be best described with the following example:

var makePowersOf = bindFirstArg(bindFirstArg, Math.pow);
var powersOfThree = makePowersOf(3);
console.log(powersOfThree(2)); // 9
console.log(powersOfThree(3)); // 27
Enter fullscreen mode Exit fullscreen mode

This is why they’re called function factories.

Partial application

Notice that our function factory example’s bindFirstArg() and bindSecondArg() functions only work for functions that have exactly two arguments. We could write new ones that work for different numbers of arguments, but that would work away from our model of generalization.
What we need is partial application.

Note

Partial application is the process of binding values to one or more arguments of a function that returns a partially-applied function that accepts the remaining, unbound arguments. Unlike the bind() function and other built-in methods of the Function object, we’ll have to create our own functions for partial application and currying. There are two distinct ways to do this.

  • As a stand-alone function, that is, var partial = function(func){...
  • As a polyfill, that is, Function.prototype.partial = function( {...

Polyfills are used to augment prototypes with new functions and will allow us to call our new functions as methods of the function that we want to partially apply. Just like this:

myfunction.partial(arg1, arg2, );
Enter fullscreen mode Exit fullscreen mode

Partial application from the left

Here’s where JavaScript’s apply() and call() utilities become useful for us. Let’s look at a possible polyfill for the Function object:

Function.prototype.partialApply = function() {
    var func = this;
    args = Array.prototype.slice.call(arguments);
    return function() {
        return func.apply(this, args.concat(
            Array.prototype.slice.call(arguments)
        ));
    };
};
Enter fullscreen mode Exit fullscreen mode

As you can see, it works by slicing the arguments special variable.

Note

Every function has a special local variable called arguments that is an array-like object of the arguments passed to it. It’s technically not an array. Therefore it does not have any of the Array methods such as slice and forEach. That’s why we need to use Array’s slice.call method to slice the arguments. And now let’s see what happens when we use it in an example. This time, let’s get away from the math and go for something a little more useful. We’ll create a little application that converts numbers to hexadecimal values.

function nums2hex() {
    function componentToHex(component) {
        var hex = component.toString(16);
        // make sure the return value is 2 digits, i.e. 0c or 12
        if (hex.length == 1) {
            return "0" + hex;
        } else {
            return hex;
        }
    }
    return Array.prototype.map.call(arguments, componentToHex).join('');
}
// the function works on any number of inputs
console.log(nums2hex()); // ''
console.log(nums2hex(100, 200)); // '64c8'
console.log(nums2hex(100, 200, 255, 0, 123)); // '64c8ff007b'
// but we can use the partial function to partially apply
// arguments, such as the OUI of a mac address
var myOUI = 123;
var getMacAddress = nums2hex.partialApply(myOUI);
console.log(getMacAddress()); // '7b'
console.log(getMacAddress(100, 200, 2, 123, 66, 0, 1)); //
'7b64c8027b420001'
// or we can convert rgb values of red only to hexadecimal
var shadesOfRed = nums2hex.partialApply(255);
console.log(shadesOfRed(123, 0)); // 'ff7b00'
console.log(shadesOfRed(100, 200)); // 'ff64c8'
Enter fullscreen mode Exit fullscreen mode

This example shows that we can partially apply arguments to a generic function and get a new function in return. This first example is left-to-right, which means that we can only partially apply the first, left-most arguments.

Partial application from the right

In order to apply arguments from the right, we can define another polyfill.

Function.prototype.partialApplyRight = function() {
    var func = this;
    args = Array.prototype.slice.call(arguments);
    return function() {
        return func.apply(
            this,
            [].slice.call(arguments, 0)
            .concat(args));
    };
};
var shadesOfBlue = nums2hex.partialApplyRight(255);
console.log(shadesOfBlue(123, 0)); // '7b00ff'
console.log(shadesOfBlue(100, 200)); // '64c8ff'
var someShadesOfGreen = nums2hex.partialApplyRight(255, 0);
console.log(shadesOfGreen(123)); // '7bff00'
console.log(shadesOfGreen(100)); // '64ff00'
Enter fullscreen mode Exit fullscreen mode

Partial application has allowed us to take a very generic function and extract more specific functions out of it. But the biggest flaw in this method is that the way in which the arguments are passed, as in how many and in what order, can be ambiguous. And ambiguity is never a good thing in programming. There’s a better way to do this: currying.

Currying

Currying is the process of transforming a function with many arguments into a function with one argument that returns another function that takes more arguments as needed. Formally, a function with N arguments can be transformed into a function chain of N functions, each with only one argument.
A common question is: what is the difference between partial application and currying?
While it’s true that partial application returns a value right away and currying only returns another curried function that takes the next argument, the fundamental difference is that currying allows for much better control of how arguments are passed to the function. We’ll see just how that’s true, but first we need to create function to perform the currying.
Here’s our polyfill for adding currying to the Function prototype:

Function.prototype.curry = function(numArgs) {
    var func = this;
    numArgs = numArgs || func.length;
    // recursively acquire the arguments
    function subCurry(prev) {
        return function(arg) {
            var args = prev.concat(arg);
            if (args.length < numArgs) {
                // recursive case: we still need more args
                return subCurry(args);
            } else {
                // base case: apply the function
                return func.apply(this, args);
            }
        };
    }
    return subCurry([]);
};
Enter fullscreen mode Exit fullscreen mode

The numArgs argument lets us optionally specify the number of arguments the function being curried needs if it’s not explicitly defined.
Let’s look at how to use it within our hexadecimal application. We’ll write a function that converts RGB values to a hexadecimal string that is appropriate for HTML:

function rgb2hex(r, g, b) {
    // nums2hex is previously defined in this chapter
    return '#' + nums2hex(r) + nums2hex(g) + nums2hex(b);
}
var hexColors = rgb2hex.curry();
console.log(hexColors(11)) // returns a curried function
console.log(hexColors(11, 12, 123)) // returns a curried function
console.log(hexColors(11)(12)(123)) // returns #0b0c7b
console.log(hexColors(210)(12)(0)) // returns #d20c00
Enter fullscreen mode Exit fullscreen mode

It will return the curried function until all needed arguments are passed in. And they’re passed in the same left-to-right order as defined by the function being curried.
But we can step it up a notch and define the more specific functions that we need as follows:

var reds = function(g,b){return hexColors(255)(g)(b)};
var greens = function(r,b){return hexColors(r)(255)(b)};
var blues = function(r,g){return hexColors(r)(g)(255)};
console.log(reds(11, 12)) // returns #ff0b0c
console.log(greens(11, 12)) // returns #0bff0c
console.log(blues(11, 12)) // returns #0b0cff
Enter fullscreen mode Exit fullscreen mode

So that’s a nice way to use currying. But if we just want to curry our nums2hex() function directly, we run into a little bit of trouble. And that’s because the function doesn’t define any arguments, it just lets you pass as many arguments in as you want. So we have to define the number of arguments. We do that with the optional parameter to the curry function that allows us to set the number of arguments of the function being curried.

var hexs = nums2hex.curry(2);
console.log(hexs(11)(12)); // returns 0b0c
console.log(hexs(11)); // returns function
console.log(hexs(110)(12)(0)); // incorrect
Enter fullscreen mode Exit fullscreen mode

Therefore currying does not work well with functions that accept variable numbers of arguments. For something like that, partial application is preferred.
All of this isn’t just for the benefit of function factories and code reuse. Currying and partial application play into a bigger pattern known as composition.

Function composition

Finally, we have arrived at function composition.
In functional programming, we want everything to be a function. We especially want unary functions if possible. If we can convert all functions to unary functions, then magical things can happen.

Note

Unary functions are functions that take only a single input. Functions with multiple inputs are polyadic, but we usually say binary for functions that accept two inputs and ternary for three inputs. Some functions don’t accept a specific number of inputs; we call those variadic.
Manipulating functions and their acceptable number of inputs can be extremely expressive. In this section, we will explore how to compose new functions from smaller functions: little units of logic that combine into whole programs that are greater than the sum of the functions on their own.

Compose

Composing functions allows us to build complex functions from many simple, generic functions. By treating functions as building blocks for other functions, we can build truly modular applications with excellent readability and maintainability. Before we define the compose() polyfill, you can see how it all works with these following examples:

var roundedSqrt = Math.round.compose(Math.sqrt)
console.log(roundedSqrt(5)); // Returns: 2
var squaredDate = roundedSqrt.compose(Date.parse)
console.log(squaredDate("January 1, 2014")); // Returns: 1178370
In math, the composition of the f and g variables is defined as f(g(x)).In JavaScript, this
can be written as:
    var compose = function(f, g) {
        return function(x) {
            return f(g(x));
        };
    };
Enter fullscreen mode Exit fullscreen mode

But if we left it at that, we would lose track of the this keyword, among other problems.
The solution is to use the apply() and call() utilities. Compared to curry, the compose() polyfill is quite simple.

Function.prototype.compose = function(prevFunc) {
    var nextFunc = this;
    return function() {
        return nextFunc.call(this, prevFunc.apply(this, arguments));
    }
}
Enter fullscreen mode Exit fullscreen mode

To show how it’s used, let’s build a completely contrived example, as follows:

function function1(a) {
    return a + ' 1';
}

function function2(b) {
    return b + ' 2';
}

function function3(c) {
    return c + ' 3';
}
var composition = function3.compose(function2).compose(function1);
console.log(composition('count')); // returns 'count 1 2 3'
Enter fullscreen mode Exit fullscreen mode

Did you notice that the function3 parameter was applied first? This is very important.
Functions are applied from right to left.

Sequence – compose in reverse

Because many people like to read things from the left to the right, it might make sense to apply the functions in that order too. We’ll call this a sequence instead of a composition.
To reverse the order, all we need to do is swap the nextFunc and prevFunc parameters.

Function.prototype.sequence = function(prevFunc) {
    var nextFunc = this;
    return function() {
        return prevFunc.call(this, nextFunc.apply(this, arguments));
    }
}
Enter fullscreen mode Exit fullscreen mode

This allows us to now call the functions in a more natural order.

var sequences = function1.sequence(function2).sequence(function3);
console.log(sequences('count')); // returns 'count 1 2 3'
Enter fullscreen mode Exit fullscreen mode

Compositions V.S chains

Here are five different implementations of the same floorSqrt() functional composition.
They seem to be identical, but they deserve scrutiny.

function floorSqrt1(num) {
    var sqrtNum = Math.sqrt(num);
    var floorSqrt = Math.floor(sqrtNum);
    var stringNum = String(floorSqrt);
    return stringNum;
}

function floorSqrt2(num) {
    return String(Math.floor(Math.sqrt(num)));
}

function floorSqrt3(num) {
    return [num].map(Math.sqrt).map(Math.floor).toString();
}
var floorSqrt4 = String.compose(Math.floor).compose(Math.sqrt);
var floorSqrt5 = Math.sqrt.sequence(Math.floor).sequence(String);
// all functions can be called like this:
floorSqrt < N > (17); // Returns: 4
Enter fullscreen mode Exit fullscreen mode

But there are a few key differences we should go over:

  • Obviously the first method is verbose and inefficient.
  • The second method is a nice one-liner, but this approach becomes very unreadable after only a few functions are applied.
Note

To say that less code is better is missing the point. Code is more maintainable when the effective instructions are more concise. If you reduce the number of characters on the screen without changing the effective instructions carried out, this has the complete opposite effect—code becomes harder to understand, and decidedly less maintainable; for example, when we use nested ternary operators, or we chain several commands together on a single line.

These approaches reduce the amount of ‘code on the screen’, but they don’t reduce the number of steps actually being specified by that code. So the effect is to obfuscate and make the code harder to understand.

The kind of conciseness that makes code easier to maintain is that which effectively reduces the specified instructions (for example, by using a simpler algorithm that accomplishes the same result with fewer and/or simpler steps), or when we simply replace code with a message, for instance, invoking a third-party library with a well-documented API.

  • The third approach is a chain of array functions, notably the map function. This works fairly well, but it is not mathematically correct.
  • Here’s our compose() function in action. All methods are forced to be unary, pure functions that encourage the use of better, simpler, and smaller functions that do one thing and do it well.
  • The last approach uses the compose() function in reverse sequence, which is just as valid.

Programming with compose

The most important aspect of compose is that, aside from the first function that is applied, it works best with pure, unary functions: functions that take only one argument.

The output of the first function that is applied is sent to the next function. This means that the function must accept what the previous function passed to it. This is the main influence behind type signatures.

Note

Type Signatures are used to explicitly declare what types of input the function accepts and what type it outputs. They were first used by Haskell, which actually used them in the function definitions to be used by the compiler. But, in JavaScript, we just put them in a code comment. They look something like this: foo :: arg1 -> argN -> output
Examples:

// getStringLength :: String -> Intfunction getStringLength(s){return
s.length};
// concatDates :: Date -> Date -> [Date]function concatDates(d1,d2){return
[d1, d2]};
// pureFunc :: (int -> Bool) -> [int] -> [int]pureFunc(func, arr){return
arr.filter(func)}
Enter fullscreen mode Exit fullscreen mode

In order to truly reap the benefits of compose, any application will need a hefty collection of unary, pure functions. These are the building blocks that are composed into larger functions that, in turn, are used to make applications that are very modular, reliable, and maintainable.
Let’s go through an example. First we’ll need many building-block functions. Some of them build upon the others as follows:

// stringToArray :: String -> [Char]
function stringToArray(s) {
    return s.split('');
}
// arrayToString :: [Char] -> String
function arrayToString(a) {
    return a.join('');
}
// nextChar :: Char -> Char
function nextChar(c) {
    return String.fromCharCode(c.charCodeAt(0) + 1);
}
// previousChar :: Char -> Char
function previousChar(c) {
    return String.fromCharCode(c.charCodeAt(0) - 1);
}
// higherColorHex :: Char -> Char
function higherColorHex(c) {
    return c >= 'f' ? 'f' :
        c == '9' ? 'a' :
        nextChar(c)
}
// lowerColorHex :: Char -> Char
function lowerColorHex(c) {
    return c <= '0' ? '0' :
        c == 'a' ? '9' :
        previousChar(c);
}
// raiseColorHexes :: String -> String
function raiseColorHexes(arr) {
    return arr.map(higherColorHex);
}
// lowerColorHexes :: String -> String
function lowerColorHexes(arr) {
    return arr.map(lowerColorHex);
}
Enter fullscreen mode Exit fullscreen mode

Now let’s compose some of them together.

var lighterColor = arrayToString
    .compose(raiseColorHexes)
    .compose(stringToArray)
var darkerColor = arrayToString
    .compose(lowerColorHexes)
    .compose(stringToArray)
console.log(lighterColor('af0189')); // Returns: 'bf129a'
console.log(darkerColor('af0189')); // Returns: '9e0078'
Enter fullscreen mode Exit fullscreen mode

We can even use compose() and curry() functions together. In fact, they work very well together. Let’s forge together the curry example with our compose example. First we’ll need our helper functions from before.

// component2hex :: Ints -> Int
function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}
// nums2hex :: Ints* -> Int
function nums2hex() {
    return Array.prototype.map.call(arguments, componentToHex).join('');
}
Enter fullscreen mode Exit fullscreen mode

First we need to make the curried and partial-applied functions, then we can compose them to our other composed functions.

var lighterColors = lighterColor
    .compose(nums2hex.curry());
var darkerRed = darkerColor
    .compose(nums2hex.partialApply(255));
Var lighterRgb2hex = lighterColor
    .compose(nums2hex.partialApply());
console.log(lighterColors(123, 0, 22)); // Returns: 8cff11
console.log(darkerRed(123, 0)); // Returns: ee6a00
console.log(lighterRgb2hex(123, 200, 100)); // Returns: 8cd975
Enter fullscreen mode Exit fullscreen mode

There we have it! The functions read really well and make a lot of sense. We were forced to begin with little functions that just did one thing. Then we were able to put together functions with more utility.
Let’s look at one last example. Here’s a function that lightens an RBG value by a variable amount. Then we can use composition to create new functions from it.

// lighterColorNumSteps :: string -> num -> string
function lighterColorNumSteps(color, n) {
    for (var i = 0; i < n; i++) {
        color = lighterColor(color);
    }
    return color;
}
// now we can create functions like this:
var lighterRedNumSteps = lighterColorNumSteps.curry().compose(reds)(0, 0);
// and use them like this:
console.log(lighterRedNumSteps(5)); // Return: 'ff5555'
console.log(lighterRedNumSteps(2)); // Return: 'ff2222'
Enter fullscreen mode Exit fullscreen mode

In the same way, we could easily create more functions for creating lighter and darker blues, greens, grays, purples, anything you want. This is a really great way to construct an API.

We just barely scratched the surface of what function composition can do. What compose does is take control away from JavaScript. Normally JavaScript will evaluate left to right, but now the interpreter is saying “OK, something else is going to take care of this, I’ll just move on to the next.” And now the compose() function has control over the evaluation sequence!
This is how Lazy.js, Bacon.js and others have been able to implement things such as lazy evaluation and infinite sequences. Up next on the second theme of this part, we’ll look into how those libraries are used along with other stuff!
Thanks for reading!

Discussion (0)