express yourself: rapid function definition

The native JavaScript function indexOf, applied to a String, returns the index of the first occurrence of a specified value. Useful as it is, I often find myself wishing for a variant method that will return the index after the occurrence so that I can pass the result to a slice call and return everything after a given character.

With this in mind, I created a method called express (as in expression, and alsoquick) which returns a new function by applying the expression argument to the result of the old function. Its partly inspired by the Haskell language and by Oliver Steele’s lambda function.

String.prototype.indexAfter = String.prototype.indexOf.express('r + 1');

(Note: this returns string.indexOf + 1, not string.indexOf + searchString.length)

The implementation is deceptively simple but packs a lot of power. The expr argument manipulates a variable r which is a proxy for the return value of the original function. I think its a nice illustration of why eval is not always evil – and how when used judiciously it can actually be a good friend. [Edit 05/12/10: Several readers have pointed out that the r reference could break down if you js minify. I've since addressed this issue using a minify-safe evalR function, which will always inject the given variable as "r", no matter what]

//minify-safe version of eval
var evalR = function(x, expr) {
    var expr = expr.replace(/\br\b/g,"arguments[0]");
    return eval(expr);
}

Function.prototype.express = function(expr) {
    var __method = this;
    return function() {
        return evalR(__method.apply(this,arguments), expr);
    }
}

Using express you can create new functions that modify the results of existing functions in almost any conceivable way.

var loudly = String.prototype.toUpperCase.express('"** " + r + "!! **"');
loudly.call("Can you hear me?"); //"** CAN YOU HEAR ME?!! **"

var randomPercentage = Math.random.express('Math.round(100*r) + "%"');
randomPercentage(); //23%

isPerfectSquare = Math.sqrt.express('r == Math.round(r)');
isPerfectSquare(25) //true

Keep in mind that when there is already an existing function that will perform the required modification, compose might be a better option (especially if you’re skittish about eval). What do you think?

//using express
var roundedSqrt = Math.sqrt.express('Math.round(r)');
roundedSqrt(34); //6

//using compose
var roundedSqrt = Math.round.compose(Math.sqrt);
roundedSqrt(34); //6

I also created a global express function in which the expression is not applied to a function result but instead manipulates the first argument directly.

var express = function(expr) {
    return function() {
        return evalR(arguments[0], expr);
    }
}

var inverse = express('1/r');
inverse(5); //0.2

var toFarenheit = express('(r*9/5)+32');
toFarenheit(10); //50

var circleArea = express('Math.PI*r*r');
circleArea(4).toFixed(2); //50.27

As with any technique, its easy to overdo it. I wouldn’t necessarily recommend using global express in production code (its perfectly robust but your team might not appreciate the unfamiliar syntax or reliance on eval). However it does illustrate the beauty of the functional approach, moreover it’s an excellent aid in debugging, testing and experimentation on the console.

Now lets go back to the indexAfter method we defined at the beginning. Here’s a nifty example that combines compose, curry and express to define a function that will extract the domain part of an email address.

var domainFromEmail =
    String.prototype.slice.compose(
        String.prototype.indexOf.express('r+1')
    ).curry('@');

domainFromEmail.call("president@whitehouse.gov"); //"whitehouse.gov"
domainFromEmail.call("mmouse@disney.com"); //"disney.com"

To me this is the height of elegance. Pure functional programming. However its worth pointing out that at this point its actually no more concise than the old fashioned way:-

var domainFromEmail = function(email) {
    return email.slice(email.indexOf('@') + 1);
}

domainFromEmail("president@whitehouse.gov"); //"whitehouse.gov"
domainFromEmail("mmouse@disney.com"); //"disney.com"

It’s a matter of preference and balance. Use the implementation that is the most expressive for you and allows for the most re-use, and be careful not to overuse any one technique.

Speaking of re-use let’s end by defining a generic function that returns everything after a character. Its useful enough to add to String prototype:-

String.prototype.sliceAfterChar = String.prototype.slice.compose(String.prototype.indexOf.express('r+1'));

var domainFromEmail = String.prototype.sliceAfterChar.curry('@');
var queryParams = String.prototype.sliceAfterChar.curry('?'); //assumes we don't want '?'
About these ads

13 thoughts on “express yourself: rapid function definition

  1. Pingback: Apple Pro Training Series: Final Cut Express 4 | 2Studio.net

  2. Cool! Brian LeRoux does something similar with eval in Lawnchair’s API: http://brianleroux.github.com/lawnchair/

    One question though, and this applies to your implementations of curry and compose as well: why implement these higher order functions as function methods rather than functions whose first argument is itself a function? Especially because Function.prototype is global. The argument against modifying global prototypes has been made a thousand times before: http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/

    Also, consider the following two code examples:

    map(function (obj) { return obj.someMethod(); },
    ____list);

    map(someFunction,
    ____list);

    Of course, some syntactic sugar would fix that extra syntactic burden (though not the unnecessary function calls) and Clojure actually has some syntactic sugar that handles this type of situation. If I were to bring that syntactic sugar in to the JS world, it might look like this:

    map(#( %.someMethod() ),
    ____list);

    Maybe its just me, but I also think that (in general) function calls look cleaner than method calls.

    I like this slightly modified version of compose that is a normal function rather than a Function.prototype method (and also takes variadic number of functions to compose):

    var compose = function (/* variadic # of functions */) {
    ____var fns = Array.prototype.slice.call(arguments, 0);
    ____return function () {
    ________var i, result = fns.shift().apply(this, arguments);
    ________for (i = 0; i < fns.length; i++)
    ____________result = fns[i].call(this, result);
    ________return result;
    ____};
    };

    What are your thoughts?

    • Nick, Yeah I knew that would be controversial. I thought long and hard before augmenting Function the first time – then after that I felt I had to keep doing it for consistency. Namespace pollution issues aside I think it looks clean (I’m allergic to too many arguments) but its very much a matter of taste.

      I’m planning an upcoming post(s) on compose and curry on steroids including chaining/muti-function compose directives and partial function (i.e. curry but with pre-assign any argument not just first n).

      Thanks for your input.

      • Sweet, looking forward to reading about how you’re beefing up the functions.

        I tend to use the implementation of partial function application that John Resig blogged about ( http://ejohn.org/blog/partial-functions-in-javascript/ ), except as a normal function rather than method.

        However, I don’t like using undefined as a placeholder (too verbose, looks funky, might actually want to pass undefined in), and a lot of other people don’t either if you read the comments on his post. I would use _ as the placeholder, but since underscore.js came out (http://documentcloud.github.com/underscore/ which you would like, if you aren’t familiar already) _ is kind of a no deal. Ended up with three underscores, ___, and I think it looks great. By binding the placeholder to an empty object, you know that you can never “accidentally” pass an object that can be compared to ___ and have the comparison return true, which is a real issue when using undefined as the placeholder.

        window.___ = {};

        // …Snip partial implementation…

        var delay = partial(setTimeout, ___, 1000);
        delay(curry(alert, “This function executes after a one second delay”));

  3. As Nick explained, I’d prefer a function callback as well.

    Function.prototype.express = function(f){
    var that = this;
    return function(){
    return f(this.apply(this, arguments);
    };
    };

    Usable like

    String.prototype.indexAfter = String.prototype.indexOf.express(function(r){ return r+1; });

    (Nicks example iterates over multiple possible function arguments)

    There’s no loss in speed (eval counts as a function call) and you’re not using eval (which could still lead to certain inconsistency). I’d say win/win :)

    • Peter, that’s just an inside-out compose.
      Its identical to:-
      (function(r){ return r+1; }).compose(indexOf) which I already covered at length in this and previous blogs.

      The purpose of the express function was to experiment with more expressive, concise function definition (as with pure functional languages like Haskell)

  4. As Andrea Giammarchi pointed out on twitter, js minifiers will wreak havoc with this code.
    There’s no guarantee that var r will be preserved after minification.

    • Good point about minifier side-effects. As I said in the post , I would not necessarily recommend this approach for production code – but I wanted to try to push the boundaries of JavaScript as a more expressive, functional language – and its a useful device for trying things out in the console, and debugging

      (btw Oliver Steele’s lambda function does something similar but without requiring eval)

  5. I recently did something similar as an extension to the Underscore library, where any function that takes an iterator function can be replaced by a string (or regex) expression. It doesn’t use ‘eval’ but ‘new Function()’ which also solves the minify problem. See: http://github.com/moos/_xiterator

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