Monday

A Java Developer's Guide to JavaScript - Functional Programming and JavaScript Mechanics

Homework Check

Last time we introduced the Prototype library. Hopefully by now you've at least looked over the Prototype API and are semi-comfortable with Firebug. If not, stop and take a few minutes to look around and write a short script or two.

Functional Programming

Functional programming is one of those things we don't really do in Java, since Java lacks first class functions. Essentially, when you can pass functions around like any other variable, you often end up sticking them together ("composing" them) to create new functions. The result of one function gets fed into another, etc. Programming effectively in JavaScript requires recognizing that many patterns that are explicit in Java are implicit in a functional language like JavaScript.

There are several 'classic' functions that are usually the basis of any good functional programming library:

map/collect
A map function takes a value and returns a new value. It's complement is reduce/inject. Here's an example using Prototype's collect function:

var foo = [1,2,3,4,5];
var bar = foo.collect(function(a) {
return a * 2;
}); // bar = [2,4,6,8,10]

reduce/inject
Takes two values (at least one is usually a collection, but they both can be) and combines them into one value. When called inject, the first value is usually the 'accumulator' and the second is a singular value. With Prototype, you'll use map/collect and inject on collections, but that's not the only way it is used. Map-Reduce is what powers Google searches. Here's an example:

var foo = [5,4,3,2,1];
var bar = foo.inject(1,function(a,b) {
return a * b;
}); // bar == 120

A little explanation for this one. The first call, the parameters are (1,5), the next call is (5,4), then next is (20,3), etc. The above is how inject works in Prototype, other versions of reduce can pass two outputs of reduce instead of the previous output and the next input. e.g., it might do (1,5), then (4,3), (2,1), resulting in 5,12,and 2, then apply the reduce function on the results and so on until we have a single value. To see how map/reduce powers Google, read Can Your Programming Language Do This?
select/findAll
Used to select/find a subset of a collection. e.g.,
[1,2,3,4,5].select(function(a) {
return a % 2 == 0;
}); // = [2,4]
all and any
Returns true if any or all of the elements in a collection match a given predicate. e.g.,
[1,2,3,4,5].any(function(a) {
return a == 4;
}); // == true

With Prototype, if you do not pass a predicate, then the value is evaluated as a boolean. You could use this to detect if a collection contains all (or any) non-zero, non-false and non-empty string values.
There are other common functions, but these cover the conceptual core. Most other functions will build off of these in some way. As you get to know the common functions, you'll discover uses for them that are extremely powerful.

Manipulating Functions in JavaScript

Before we talk about design patterns, we need to quick take a look under the JavaScript hood and see how we can manipulate the scope and arguments of functions. Remember that when we call a function as a member of an object, the this keyword lets us access variables in the scope of that object. But if you have only a reference to a function and you try to call it, this will refer to the global (window) scope. To allow us to set the scope of a function, we can use call. Here's an example:
var foo = { a: 1};
function aPlus(b) {
this.a = this.a + b;
return this.a;
}
aPlus.call(foo,5); // == 6 && foo.a == 6
This would be almost equivalent to
foo.aPlus = aPlus;
foo.aPlus(5);
However, the second example will overwrite aPlus on foo. If aplus happens to be an existing variable or method, we've now overwritten it.

call
can be used whenever you know the exact number of arguments you'll be passing to a function, but what if you don't? You might say, "When would that happen?" What if you have a function that takes a function as an argument, and returns another function? This is something we do a lot in functional programming, so we need a way to keep our function producing functions DRY.

Before we discuss how you would call a function without knowing the number of arguments, we need to talk about a special variable: arguments. Inside any function, the arguments variable is automatically defined. arguments is an array-like structure that has all the arguments passed to the function. In JavaScript, the parameters you declare fo a function are just convenient labels for indecies into arguments. The JavaScript interpreter doesn't care if you call the function foo(a,b) with 1,2, or 7 arguments. a = arguments[0] and b = arguments[1], and if arguments[1] is undefined, so is b. Like an array, arguments has a length, so you can iterate over it. e.g.,
function sum() {
var sum = 0;
for(var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
Suppose we also have this function:
function double(a) {
return a * 2;
}
and we want to create a function that takes the sum of 2 or more numbers and returns twice the result. To reuse our existing functions, we need some way of creating a function that passes all of it's arguments to sum regardless of the number of arguments passed to it. apply to the rescue! Like call, apply lets you call a function and set the value of this, but instead of taking the arguments from the 2nd argument on, it takes an array (or array-like object) as the second parameter. e.g.,
function doubleSum() {
return double( sum.apply(this,arguments) );
}
doubleSum(1,1,1,1,1); // == 10
doubleSum(1,2,3); // == 12
doubleSum(); // == 0
We haven't made use of Prototype here yet because we haven't needed it. However, you may have noticed that we keep referring to arguments as an "array-like" object. That's because it's not a real Array and doesn't have any of the instance methods you can expect an array to have. Prototype provides a utility function for just this purpose, $A. $A(arguments) returns a true array with all of the arguments in it, so you can call all the standard Array methods and Prototype methods on it. We can rewrite sum like so:
function sum() {
return $A(arguments).inject(0,function(a,b) {
return a + b;
});
}
Using apply we can even write call:
function myCall(func,scope) {
var args = $A(arguments).slice(2); // creates a copy of arguments from index 2 on
return func.apply(scope,args);

};
That covers call and apply. Next time we'll see how we can use what we now know about functional programming and JavaScript mechanics to implement the design patterns we know and love from Java.

1 comment:

Android app developers said...

This is one of the unique and special post.I like your blog tips.This is one of the good post.