Depends which parts of functional programming you mean. I use $.each (JQuery), closures, let-bindings, and anonymous functions all the time. Not so much on the statelessness, because:
a.) JavaScript makes it a bit of a pain to deep-copy objects; you can rig up something with $.extend and $.map, but it's not in the standard language the way Python's .copy() and [:] are
b.) Most of the time when you're doing JavaScript, the point is to change the state of the UI. Hard to write stateless code where the intended effect is to change state...
(function(var1, var2) {
// var1 and var2 are now bound within this block
})(val1, val2);
Why use this instead of...
var var1 = val1;
var var2 = val2;
// do stuff with var1 & var2
It's because if you have any closures within the block, they'll capture the bindings of var1 and var2 instead of their values. That means that if you're inside a loop, your bindings will be overwritten each time through, so you could end up creating a dozen closures that all do the same thing.
Here's an example from a JQuery patch I did a couple weeks ago:
Before it used an eval statement to directly write the value into the source code of the closure. After, it captures the value with a let-binding and then returns a closure over the current value instead of the current binding.
Thanks. I thought that someone was suggesting that they were using let = ... syntax. Now, I understand that the idiom you showed is "let binding" in the Javascript world.
I came to Javascript from Ruby/Scheme/Haskell, so when I had to call the Yahoo Search API with a callback, sometimes with two or more searches concurrently, I thought, oh, I'll store the continuation, because they only allow /[A-Za-z0-9.]+/ as characters in the callback.
var fns = [];
function gensym() { return $R(0,20).toArray().map(function(){return String.fromCharCode(Math.random()*26+65)}).join('') }
function storefunc(f) { var g = gensym(); fns[g] = f; return 'fns.'+g }
function jsonk(url,hash,k) {
var s = document.createElement('script');
I learned gensym and web-read-k in Scheme at Brown (thanks, Shriram!), and jsonk turned out to be pretty useful, because the same comtinuation-passing style that's available when you do XHRs now becomes available when you do json requests from a different domain, if they have a callback parameter. (I brought this code to Songkick, and we shortened callback to k.)
fns stores continuations.
gensym: make an array of 20 random characters (make an array of 20 #s, map each to a random character).
storefunc: continuation -> string, and store the continuation in fns, such that eval(string)==continuation
jsonk: make a query string from the url, the parameters supplied, and the continuation (change 'callback' to whatever the api calls it), and then add that script to the page, returning the url for debugging purposes.
you could also have a timeout after 30 seconds to say "something went wrong", and that might not be much more code, just set a timeout, and wrap k in jsonk to a function that first stops the delayed error message and then calls k, but that's an exercise for the reader.
It uses the functional reactive pardigm where state can be implicitly updated without callbacks. Flapjax is compiled down to javascript, which makes a lot more sense than the functional javascript library which tries to make javascript into something it isn't.
anyone have any suggestions as to how i can make this a little more functional? it's the code for the score ticker on my site, http://www.qtoro.com. (uses the prototype js framework.) it was feeling pretty good til i had to put in those 'if' statements...
Urtog.score = new function() {
this.init = function() {
this.upmod; // boolean. if increasing score, it's true; if decreasing, false.
this.unit = 34; // the pixel height of one digit
this.digits = $$('div#scoredigits img.digit'); // array of all the digit images
}
/* change the score.
* first arg is the # of points to add or subtract (if negative).
* second arg is optional, specifies which digit img element to mod. the default is the "1's" digit.
*/
this.mod = function( points, digit ) {
this.upmod = (points > 0);
digit = digit || this.digits.last();
var di = this.digits.indexOf(digit);
var dy = (this.upmod ? 0-this.unit : this.unit);
(Math.abs(points)).times(function(i) {
new Effect.MoveBy(digit, dy, 0, {
queue: {position:"end", scope:"d"+di},
duration: 0.075,
transition: Effect.Transitions.linear,
beforeSetup: preroll.bind(Urtog.score)
});
})
}
/* the callback that's run before the digit is modded.
* does two things:
* 1. resets the image strip to the top/bottom if necessary
* 2. checks if we should simultaneously mod the 10x digit (e.g. going from 09=>10 or 10=>09)
*/
function preroll(effect) {
var d = effect.element;
var di = this.digits.indexOf(d);
var offset = d.style.top;
if (this.upmod) { // adding points
if (offset=="-340px") { // at the bottom of the strip, so reset to the top
d.style.top=offset="0px";
}
if (offset=="-306px") { // transitioning from 9=>0, so mod the 10x digit, too
this.mod( 1, this.digits[di-1] );
}
}
else { // subtracting points
if (this.digits.all(function(d) { return is_zero(d.style.top); })) { // we've hit zero!
Effect.Queues.instances.keys().each(function(k) {
if (k.match(/d[0-9]*$/)) { // cancel all pending digit mods -- digit effect queue names are like d3,d2,d1
Effect.Queues.instances[k].each(function(e) {
e.cancel()
});
}
});
new Effect.UrtogShuffle('scoredigits'); // :)
return false;
}
if (is_zero(offset)) { // at the top of the strip, so reset to the bottom
d.style.top=offset="-340px";
}
if (offset=="-340px") { // transitioning from 0=>9, so mod the 10x digit, too
this.mod( -1, this.digits[di-1] );
}
}
}
// account for bizarre safari behavior... wtf is "-0px"??
function is_zero(offset) {
return (offset=="0px" || offset=="-0px");
}
} // end Urtog.score
a.) JavaScript makes it a bit of a pain to deep-copy objects; you can rig up something with $.extend and $.map, but it's not in the standard language the way Python's .copy() and [:] are
b.) Most of the time when you're doing JavaScript, the point is to change the state of the UI. Hard to write stateless code where the intended effect is to change state...