Curry Functions
In the midst of discussing my runnables technique for implementing function pointers over on the Rausch Generator Blog, I commented about the usefulness of the currying technique, mostly in callbacks. It occurred to me that I’ve been using a tiny helper class to curry functions for years in both AS2 and AS3, but that many people may have never stopped to write such a basic (in my opinion) class. So I figured I might as well re-implement the class and post it here.
The idea behind the class is that oftentimes you would like to pass along some data you have to a function in addition to what the function would normally be passed. For example, an event listener function takes only one argument: the Event. If you have some data handy as a local variable, you normally would need to store this as a field and retrieve it later. This is clumsy and results in lots of extra code which bloats your SWF size. Currying can greatly help you here! Consider some pseudo-code for a sprite that displays a student portrait and name as a single bitmap without using currying:
class StudentViewer extends Sprite { private var __student:XML; function StudentViewer(url:String): void { loadXML(url, onStudentLoaded); } function onStudentLoaded(student:XML): void { __student = student; loadImage(student.portrait, onPortraitLoaded); } function onPortraitLoaded(portrait:DisplayObject): void { var tf:TextField = new TextField(); tf.width = portrait.width; tf.text = __student.first + " " + __student.last; var bmd:BitmapData = new BitmapData(portrait.width, portrait.height); bmd.draw(portrait); bmd.draw(tf); addChild(new Bitmap(bmd)); __student = null; } }
The downside here is that the student XML needs to be manually stored in a field and cleaned up later to free the memory. Let’s see how currying helps this situation:
class StudentViewer extends Sprite { function StudentViewer(url:String): void { loadXML(url, onStudentLoaded); } function onStudentLoaded(student:XML): void { loadImage(student.portrait, CurryFunction.create(this, onPortraitLoaded, student)); } function onPortraitLoaded(portrait:DisplayObject, student:XML): void { var tf:TextField = new TextField(); tf.width = portrait.width; tf.text = student.first + " " + student.last; var bmd:BitmapData = new BitmapData(portrait.width, portrait.height); bmd.draw(portrait); bmd.draw(tf); addChild(new Bitmap(bmd)); } }
Here we have moved the storage of the student XML from a temporary field of our class to the callback that will be using it. The callback can now simply use what it is provided rather than having to know where to look up the pertinent information. It also unburdens the callback from the duty of cleaning up the temporary field. The callback no longer needs to concern itself with cleanup; it simply does the work that it is supposed to do.
So how is all of this achieved. Well, closures make this a complete snap in AS2 and AS3. I will provide the AS3 version of the class here but leave creation of the AS2 version as an exercise for those still concerned with using it. The differences are mostly syntactical anyhow. Here is the AS3 version:
package { /** * A utility class to create curried functions. These 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)(); * private function foo(val:Number, val2:Number): void * { * // val is 3, val2 is 5 * } * While it is harmless to do so, it is pointless to directly instantiate * a CurryFunction. * @author Jackson Dunstan (jackson@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)); }; } } }
For those new to functional programming, this can be a little mind-bending. It’s important to remember here that you never actually have a CurryFunction object. CurryFunction is simply a class that is used to create curried functions. CurryFunction creates an in-between function that will call your actual function and pass not only the arguments it got but also all the arguments that you are “currying in”. Here is an example app that tests out CurryFunction:
package { import flash.text.*; import flash.display.*; public class MyApp extends Sprite { private static var logger:TextField = new TextField(); public function MyApp() { logger.autoSize = TextFieldAutoSize.LEFT; logger.multiline = true; addChild(logger); CurryFunction.create(this, foo, 3, 5)(); log("goo returned: " + CurryFunction.create(this, goo, "korma")()); CurryFunction.create( null, function(bool:Boolean, integer:int): void { log("void dynamic func: bool=" + bool + ", integer=" + integer); }, true, 42 )(); log("non-void dynamic func returned: " + CurryFunction.create( null, function(num:Number, x:XML): RegExp { log("non-void dynamic func: num=" + num + ", x=" + x); return new RegExp("^monkey$"); }, Math.PI, <person><first>Jackson</first><last>Dunstan</last></person> )()); } private function foo(val:Number, val2:Number): void { log("foo: val=" + val + ", val2=" + val2); } private function goo(str:String): String { log("goo: str=" + str); return str; } public static function log(o:*): void { logger.appendText(o + "\n"); } } }
And the results, all as expected:
foo: val=3, val2=5 goo: str=korma goo returned: korma void dynamic func: bool=true, integer=42 non-void dynamic func: num=3.141592653589793, x=<person> <first>Jackson</first> <last>Dunstan</last> </person> non-void dynamic func returned: /^monkey$/
One downside to currying is that it is not as fast as regular functions. It makes one dynamic function call (the call through a Function variable) into two dynamic function calls (the middle-man function) and also does an Array.concat(), which is dynamic and causes a bit of memory allocation. All of this is necessary in order to do the currying, but is certainly not fast. I think the best argument against this downside is that if you are using dynamic function calls rather than my runnable technique, you probably don’t care much about performance anyhow. You may care about it elsewhere in your app, but not in the area that you’re using dynamic function calls.
As I said in the opening, this is a very valuable tool to have available to you. It will save you a lot of typing and therefore increase your productivity in many cases. In my daily AS3 programming I use currying very often, always in non-performance critical areas of my code. I recommend to you all that you add the CurryFunction class to your codebase. If you never use it, it will not be compiled into your SWF and you will have lost nothing. I believe that just knowing that the tool is there for use will remind you that it would be handy some day. On that day you will use it and be just a little bit more productive. Enjoy!
#1 by Sandro Manke on September 28th, 2009 ·
Hey Jackson
your currying looks a lot like the “delegate” which was/is widely used and you’re right – the delegate there is basically more a currying, than a delegate, but the name “curry” is maybe not so cool :)
btw, our CallbackCommand was a way of doing this currying easily the old way like here, but using the convenience, that we could also optimize this by using “hand made” commands, which will be completly typed.
one short example of doing this completely typed would be to make an XmlLoadedCommand(loadedCallback:ILoadedCallback,data:XML);which you’ll hand over
your caller will then implement the Interface and this way you could call him using a real function, as it is defined in the interface. As this is not always convenient for “small local code” you might not want to do it always, but its a choice you’ll have.
I’d even go so far to extend this even more and put all the stuff into one(or more) commands. so your function
onPortraitLoaded(portrait:DisplayObject, student:XML): void
would actually be completly wrapped in the execute(); method of the command.
it may seem strange but it helps giving out minimal interfaces and then doing the real work in “workers” which you can re-use later for a lot of objects. This also helps to keep your code small.
#2 by Sandro Manke on September 28th, 2009 ·
one addition maybe, which i encounter quite often. I tend to start off my code like you do here but at some point i see, that i have some classes doing lots of the same stuff like this – by already using the ICommand pattern i can easily externalize this into a real command and kill all the remaining curry-calls which i then do not need anymore. you could also do this with runnables, but you’d have to use them from the beginning. Basically its really just more about thinking “i might wanna use this later on globally, but i dont care for now”
#3 by jackson on September 28th, 2009 ·
These are good points. I remember the Delegate and Proxy classes in AS2, but I never used them because I had my own and there was no benefit to using Macromedia’s version. Those are gone now in AS3 and I wonder how many programmers who started on AS3 never knew about them. This article is for anyone who wants to use this very effective and easy-to-use technique. I agree that commands (or runnables) could be better, but I think of them as a more heavyweight approach as they require much more of an investment in terms of typing, class creation, allocation, and so forth. They do scale well though as you can use them throughout your code and have less memory overhead if you do. But for quickly adding on a parameter or two, nothing beats the convenience of curry.
#4 by Alan Shaw on January 29th, 2010 ·
Very cool technique; just one thing though: I believe this is not currying but uncurrying, as described in the Wikipedia entry you’ve linked to!
#5 by jackson on January 30th, 2010 ·
You’re right! I still like to use the term “curry” to refer to both adding and removing arguments. Like I said in the article, I like to express that as “currying in” and “currying out” arguments. Perhaps this is just me though. :)
#6 by Alan Shaw on January 30th, 2010 ·
Haha, I tend to think it’s more natural for “currying out,” maybe because it reminds me of “cull.” Of course the fun fact about it is that it’s named after Haskell Curry, as is also the Haskell programming language!