Compose: functions as building blocks (original) (raw)

As regular readers have probably noticed, a recurring theme of these posts is function manipulation as an expressive device. JavaScript treats functions as first class objects, that is they can be created and modified dynamically and passed as data to other functions and objects. Shamelessly continuing this theme, allow me to introduce functional composition…

Here’s a couple of simple examples to start:-

var alertPower = alert.compose(Math.pow); alertPower(9,8); //alert shows 43046721

var roundedSqRoot = Math.round.compose(Math.sqrt); roundedSqRoot(28); //5

The compose function allows us to define a new function based on two existing (or anonymous) functions. In generic form:

myFunction = function1.compose(function2);

and when we call…

myFunction(myArgs);

…function 2 gets invoked with myArgs and the result is passed to the invocation of function 1. Like curry, compose is not a native JavaScript function but its easy to augment the Function prototype to support it.

Function.prototype.compose = function(argFunction) { var invokingFunction = this; return function() { return invokingFunction.call(this,argFunction.apply(this,arguments)); } }

Now a meatier example – this creates a rudimentary parseAlpha function (it also makes use of the curry function described in an earlier post) :-

//use curry to make a slice(0,x) function, then use compose to wrap it around search. var sliceToRegEx = String.prototype.slice.curry(0).compose(String.prototype.search);

//now curry with a regEx that returns first non alpha character var parseAlpha = sliceToRegEx.curry(/[^ a-zA-Z]/); parseAlpha.call("Pork Bellies #45678"); //Pork Bellies

Compose and curry often work well together – forging new functions from old, in a concise and readable way. Here’s another example:-

var queryString = String.prototype.substring.compose(String.prototype.indexOf).curry('?'); queryString.call("http://www.wunderground.com?query=94101&weekday=Tuesday"); //?query=94101&weekday=Tuesday

Going deeper, the following function loops through an Enumerable looking for the longest sequence for which the given function holds true (notice I’m ignoring blank string members) :-

var longestSequence = function(compareFunc,myEnum) { var result = {member:null, count:0}; var thisCount = 1; for (var i=1; i<myEnum.length; ++i) { if ((myEnum[i]!==" ") && compareFunc(myEnum[i-1], myEnum[i])) { if (++thisCount >= result.count) { result = {member: myEnum[i], count: thisCount}; } } else { thisCount = 1; } } return result.member + " (" + result.count + ")"; }

So for example, to look for the longest successive run of equal members

longestSequence(function(a,b){return a==b},'skiing'); //i (2)

We can use curry to create a reuseable function to get the longest run of the same member.

var longestEqualRun = longestSequence.curry(function(a,b){return a==b}); longestEqualRun([1,1,2,2,2,2,3,3]); //2 (4)

Now here comes compose…and voilà…we have a function to return the most frequent member:-

var mostFrequent = longestEqualRun.compose(function(myEnum){return myEnum.split('').sort()}); mostFrequent("The quick brown fox jumps over the lazy dog"); //o (4)

How about a function to return the most frequent character in the current page source? No problem – just compose once more:-

function getInnerText(elem) { return elem.innerText || elem.textContent;     }

var mostFrequentInPage = mostFrequent.compose(function() {return getInnerText(document.body)}); mostFrequentInPage(); //e (263)