Semantically Rich and Expressive JavaScript

One of the things that impresses me about JavaScript is how functions and closure allow for the creation of semantically rich and expressive code.

Recently I needed to develop code that could make a number of transformations on an image. The transformations were driven by ranges of numbers which were entered into the system by the user. These numbers represented a superset of transformation parameters and the software had to be able to create subsets of these numbers based on filters or predicates entered by the user. In addition, the actual transformations were selectable at runtime so even the function called to do the actual transformation had to be dynamic.

At first glance this seemed as if it would take lots of code and lots of time to be able to provide an implementation that could handle the dynamic nature of the problem domain. When my brain gets bogged down and starts to sizzle like bacon hitting a hot frying pan I like to go for long walks which helps me think. Well the fresh air helped and I realized that I could take advantage of functions (as fist class citizens) and closure to provide a solution that wouldn’t require lots of code, that could also be made generic and semantically eloquent as well.

Implementation

The code below is a simplified implementation of the actual code but minus all the domain specific details. So, instead of a real image object I am using an object literal to mock a real image object and instead of obtaining transformational values, predicates and the transformations from the user I am hard coding them here. But in every other regard, this is the code that the application is actually using.

            (function ( window ) {

                "use strict";

                /**
                 * Using functions and closure to
                 * create idiomatic constructs.
                 **/

                var makeForEachFilter = function ( filterFunc ) {
                    return function ( numberToCheck, index, array ) {
                        return filterFunc( numberToCheck );
                    }
                };

                /*
                 * A make believe image object with a transform
                 * method which takes an array of values.
                 */
                var image = {
                    transform : function ( values ) {
                        // now transform the image using somehow the new numbers in the process ...
                        console.log( "transforming image using values: " + values );
                    }
                };

                /*
                 * A function that generates a subset of
                 * values filtered from a superset and
                 * then calls a method with the generated
                 * subset.
                 */
                var doWith = function ( valuesToTransform, filter, transformationFunction ) {
                    var newVals = [];
                    valuesToTransform.forEach( function ( value, index, array ) {
                        if ( filter( value ) ) {
                            newVals.push( value );
                        }
                    } );
                    transformationFunction( newVals );
                };

                /*
                * A superset of values from which subsets are made
                */
                var valuesToFilter = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048];

                /*
                 * Do something with a subset of numbers
                 * that are all > 100
                 */
                var greaterThanOneHundred = makeForEachFilter( function ( numberToCheck ) {
                    return numberToCheck > 100;
                } );

                doWith( valuesToFilter, greaterThanOneHundred, image.transform );

                /*
                 * Do something with a subset of numbers
                 * that are all < 100
                 */
                var lessThanOneHundred = makeForEachFilter( function ( numberToCheck ) {
                    return numberToCheck < 100;
                } );

                doWith( valuesToFilter, lessThanOneHundred, image.transform );

                /*
                 * Do something with a subset of numbers
                 * that are > 4 & < 100
                 */
                var betweenFourAndOneHundred = makeForEachFilter( function ( numberToCheck ) {
                    return numberToCheck > 4 && numberToCheck < 100;
                } );

                doWith( valuesToFilter, betweenFourAndOneHundred, image.transform );

            }( window ));

The makeForEachFilter method is perhaps the most interesting of all the code in that it creates a closure whereby the inner function can access the filter function after its parent function is no longer in scope. So makeForEachFilter is really a filter factory — you hand it a function that acts as a filter and it returns a function that can filter numbers.

The remaining code is split between the doWith function and code to demo the implementation. As you can see, when doWith is called, it is passed an array of numbers which represents the superset (in the real application, these numbers represented all the transformational parameters that could be applied to an image), a filter function (one created by the makeForEachFilter method) used to create a subset of the superset, and a function that does something with the subset of numbers (in the real world, image objects have their own method to handle requests to do transformations on themselves). That’s a lot of functionality from 30 or so lines of implementation code, minus whitespace.

JavaScript isn’t Clojure and nor is it Scala but it certainly has some common roots with both of these Lisp and functional languages, respectively, and those are closure and first class functions which allows using JavaScript to create the kind of implementations as the one I present here.

Summary

It isn’t a stretch to suggest that understanding functions as objects (as first class citizens) and closure are key to mastering JavaScript.  If either or both of these concepts are foreign to you then may I humbly suggest that you haven’t really learned JavaScript, and that you are one of “those” people that Douglas Crockford talks about in his tutorials. Honestly, though, not too long ago I was one of “those” too. So all I can say is, “if Jeff can really learn to code JavaScript in a manner that even Douglas Crockford might noddingly approve of, so can you!”

:q

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s