the tri-lambs (λλλ)
<< Apr 1, 2006 @ 00:28 >>
Ha! Learning Scheme in CSci 1901 proves useful! Somewhere deep in the recesses of my mind, functional programming knowledge endures. It's a good thing I paid attention in that class. Actually, out of the 150 or so students taking the class that semester, I got the highest grade. (The prof emailed me to tell me. You rock Maria!) So I did a little more than just pay attention.
Man, I used to be so awesome... what happened?
Anyway, I'm writing a widget for the OS X Dashboard. Typically these things are written in Javascript, and I discovered an interesting language problem...
I have count <div>'s, and each one has a unique number associated with it. When I click on the <div> I need to know the unique number of the <div> that was clicked... and I'm creating these things dynamically by adding them to the DOM. I'm not doing any document.write() crap. So, I thought I'd be clever and when I create the <div> I will overwrite its onclick method with a lambda function that calls my clickHandler() function while passing in the unique number for the <div>. Something like this:
for ( var i = 0; i < count; i++ )
{
var div = document.createElement( 'div' );
div.onclick = function () { clickHandler( i ); };
container.appendChild( div );
}
That looks like it should work. Seems logical. The problem is that clickHandler( i ); is the body of a function, so it is not evaluated at the time of this assignment, and the i is not evaluated to its numerical value. It remains a reference to the variable i in the scope of this for loop.
What does that mean? Well, every time clickHandler() is called, i has the exact same value, since it is a reference to the same variable. i evaluates to count to be exact, because that is what i equals at the end of the for loop.
So shit, what do I do about that? I was initally wondering about dereferencing i to its actual value, but that wouldn't have mattered because the dereference also wouldn't have been immediately evaluated. There would have been the same problem.
Luckily, the solution just popped right into my noggin! Another lambda function ... that returns our first lamda function! So this is what it needs to look like:
for ( var i = 0; i < count; i++ )
{
var div = document.createElement( 'div' );
div.onclick = ( function ( i ) { return function () { clickHandler( i ); }; } )( i );
container.appendChild( div );
}
So what the hell is going on there, and how does that work? Well, lets look at this part first:
function ( i ) { return function () { clickHandler( i ); }; }
So there we have a lambda function that takes one argument i, and this function returns our original lambda function. Yeah... you can do that.
But we don't want to override the <div>'s onclick method with this new lambda function! Because if we did, rather than calling clickHandler() when we click the <div>, instead you'd be creating lambda functions when you click... and that's just crazy (or genius... click to create functions... hmmmm).
Actually, we want to call/evaluate this lambda function in place, so that it returns our lambda function whose body calls clickHandler() and that gets assigned to the <div>'s onclick method.
You're getting all this right?
And the way you call a lambda function in place is by slapping some parens around it, pretending that is a function name, and then adding on the usual parens with args in them at the end. Here, let me give you an example:
function mySquare( n )
{
return n * n;
}
mySquare( 3 ); // will return 9
So that's a named function, and here's the same thing using a lambda function:
( function (n) { return n * n; } )( 3 ); // will return 9
You get it now?
Anyway... I've totally gotten away from the point. The magic part is that passing i as an argument into the new lambda function we wrapped around our original lambda function means that i gets evaluated at that moment! And so the lambda function returns our original lambda function with a new i in its scope that has the correct value of i. Problem solved!
I totally rushed through the important part, didn't I?
Reader Comments...
April 1, 2006 @ 01:13:21
xopl (#001)
Nevermind the fact that each <div> could have been given an id attribute that I would have had direct access to... that's too easy!
April 2, 2006 @ 21:40:25
paularms (#1017)
I'm too lazy to read all of this. More pictures.
April 3, 2006 @ 13:15:06
Christy (monster)
This entry was HOTT.
However, someday I hope you will learn that "it's" means "it is" and is not possessive. You're making my english teacher grandpa cry. You don't wanna make my grandpa cry, do you?
April 3, 2006 @ 14:03:15
xopl (#001)
I do know that... but my brain usually ignores such things when I'm rambling on about lambda functions.
April 3, 2006 @ 17:33:07
Christy (monster)
Hey, it happens.