As a followup to last week’s article on curry functions, today’s is about the reverse of currying.

If you try to pass too many arguments to a function, you’ll get a runtime error. Consider this example:

private function foo(): void
{
	var f:Function = addTwo;
	f(1, 2, 3);
}
 
private function addTwo(val1:Number, val2:Number): Number
{
	return val1 + val2;
}

Which yields the runtime error:

ArgumentError: Error #1063: Argument count mismatch on addTwo(). Expected 2, got 3.

The reason for the Function variable is that the compiler will do error checking on the number of arguments you pass, but it will not do this error checking on a Function variable. There are many times where you will use a Function variable though, such as callbacks or with some of the Flash API’s functions. Usually, you don’t get to control what gets passed to your function and often you have a function that you don’t want to add useless parameters on to. One way to deal with this is reverse currying. Thankfully, this is easy to do:

private function foo(): void
{
	var f:Function = addTwo;
	f(1, 2, 3);
}
 
private function addTwo(val1:Number, val2:Number, ...rest): Number
{
	return val1 + val2;
}

All of the extra parameters will be held in the rest variable, but you can safely ignore them. If only two parameters are passed to addTwo, rest will simply be empty. Var args is really working in our favor here. We can make a companion function to CurryFunction.create to do the reverse currying for us:

package
{
	/**
	*   A utility class to create curried and reverse-curried functions.
	*   
	*   Curried functions are functions that will call another function and add
	*   extra parameters that you define when you create the curried function.
	*   For example:
	*      CurryFunction.create(this, foo, 3, 5)();
	*      function foo(val:Number, val2:Number): void
	*      {
	*          // val is 3, val2 is 5
	*      }
	*
	*   A reverse-curried functions are functions that will call another
	*   function and either remove parameters down to some level or pad
	*   parameters with undefined.
	*   For example:
	*      CurryFunction.reverse(this, addTwo, 2)(1, 2, 3, 4, 5);
	*      function addTwo(val1:Number, val2:Number): Number
	*      {
	*          // 3, 4, and 5 are not passed in
	*          return val1 + val2;
	*      }
	* 
	*   While it is harmless to do so, it is pointless to directly instantiate
	*   a CurryFunction.
	*   
	*   @author Jackson Dunstan ([email protected]jacksondunstan.com)
	*/
	public class CurryFunction
	{
		/**
		*   Create a curried function
		*   @param thisArg The object to call the function on, usually "this" or
		*				  "null" for dynamic functions, static functions, or
		*				  functions outside a class.
		*   @param func Function to call
		*   @param extraArgs Arguments to curry in. These will be passed after
		*					the arguments normally passed to the function.
		*/
		public static function create(thisArg:Object, func:Function, ...extraArgs): Function
		{
			return function(...args): *
			{
				return func.apply(thisArg, args.concat(extraArgs));
			};
		}
 
		/**
		*   Create a reverse-curried function
		*   @param thisArg The object to call the function on, usually "this" or
		*				  "null" for dynamic functions, static functions, or
		*				  functions outside a class.
		*   @param func Function to call
		*   @param numArgs Number of arguments to allow through to the function.
		*                 This can be used to increase the number of arguments
		*                 passed to the function, but all arguments beyond
		*                 those actually passed will be undefined. 
		*/
		public static function reverse(thisArg:Object, func:Function, numArgs:uint): Function
		{
			return function(...args): *
			{
				args.length = numArgs;
				return func.apply(thisArg, args);
			};
		}
	}
}

This is rather easy to use:

private function foo(): void
{
	var f:Function = CurryFunction.reverse(this, addTwo, 2);
	f(1, 2, 3);
}
 
private function addTwo(val1:Number, val2:Number): Number
{
	return val1 + val2;
}

As with regular currying, there is some overhead to this functionality. This creates an additional dynamic function that acts as a middleman between the caller and the actual function that has been reverse curried. That is expensive, so you should make sure you don’t use currying or reverse currying in any performance-critical code. Using ...rest is faster since it doesn’t involve a middleman function, but will add a little performance hit too. Optimally, performance critical code should always pass the correct number of parameters.

If you added CurryFunction to an AS3 library, go ahead an update it to support reverse currying. If you haven’t added it yet, now you have one more reason. Again, I encourage having more tools in your toolbox because you never know when they’ll come in handy. MXMLC will even drop the CurryFunction class if you never use it, so there’s really little reason not to. I hope this comes in handy for some of you, it sure has for me!