Mar 19, 2011

A enhanced curry method

JavaScript is a functional language, it supports function composition, we can do curry with JavaScript. Douglas Crockford has defined "curry" method in his book JavaScript: The Good Parts to facilitate the steps to define curried function.

Function.prototype.method = function ( name, func ) {
 if ( !this.prototype[name] ) {
  this.prototype[name] = func;
 }
};

Function.method( 'curry', function () {
 var slice = Array.prototype.slice,
   args = slice.apply( arguments ),
   that = this;
 return function () {
  return that.apply( null, args.concat( slice.apply( arguments ) ) );
 };
} );

//so I can write the following code

test( "test curry", function () {
 function cancat_abc( a, b, c ) {
  return a + b + c ;
 }

 var cancatBd = cancatAbc.curry( "a" );
 var result = cancatBc( "b", "c" );
 equal( result, "abc", "the first parameter is preloaded" );

} );


Nice. But there is problem here, the preloaded arguments have to be the first consecutive arguments. So if I have want to preload the second and third parameter. The curry method does not support that.


Of course, I can do it manually instead of using the curry method, but this is not quite DRY (Don't Repeat Yourself). So I modify the curry method as follow to do the work.


Function._ = {};

Function.method( 'curry', function () {
 var slice = Array.prototype.slice,
   preloads = slice.apply( arguments ),
   _ = Function._,
   fn = this;
 return function () {
  var args = slice.apply( arguments );
  for ( var i = 0; i < preloads.length; i++ ) {
   if ( preloads[i] == _ ) {
    preloads[i] = args.shift();
   }
  }

  return fn.apply( null, preloads.concat( args ) );
 };
} );

var _ = Function._;

test( "test curry", function () {
 ok( _, "an dummy object has been defined" );
 function cancat_abcd( a, b, c, d ) {
  return a + b + c + d;
 }

 var cancatBd = cancatAbc.curry( "a", _, "c", _ );
 var result = cancatBd( "b", "d" );
 equal( result, "abcd", "curry should work in order" );

} );

No comments:

Post a Comment