Tuesday, October 23, 2007

__NoSuchMethod__

Because I always forget what it's called. Surely 'method_missing' is more obvious. (__NoSuchMethod__ is the javascript function that does the same thing as method_missing in Ruby)

Monday, October 15, 2007

Objective Javascript

So my next project is to write a JS parser that will turn

{ my_alert: alert_string | alert(alert_string); }

[map: my_alert onto: \["a","b","c"]]

[node addEvent("click", {event| alert('whatever'); }) ]

into

function my_alert(alertString) { alert(alertString); }

map$onto(alertString, ["a","b","c"]);

node.addEvent("click", function(event){ alert('whatever'); });

yadda ya think? not the best examples...

I just want to introduce named parameters (not using a map!) and quick anonymous function definitions. (So why do I call it objective...)

Is it Javascript...or Lisp?

Well...

lisp(
reduce(
if$(true, add, sub),
map( add_one, [1,2,3])
)
);
// prints 9

...which is it?

Well, of course it's javascript, but I got pretty close hey? The lisp function triggers the inner stuff to evaluate (lazily) and look at least a little lisp like. Note that the if will only evaluate the 'successful' branch - so 'sub' will not even be evaluated!

It all falls down a bit when you try to define a function. The best I could do was something like

function add_one(a_value) {
return add(1, a_value);
}

which is a lot more verbose than lisp, but not tooo many more elements.

Here's the code. First up, the foundations

function LispExpression(originalExpression, expression_arguments) {
this.originalExpression = originalExpression;
this.expression_arguments = expression_arguments;
}

LispExpression.prototype.evaluate = function evaluate(){
return this.originalExpression.apply(null, this.expression_arguments);
}

function lisp(lispExpression){
if( lispExpression instanceof LispExpression) {
return lispExpression.evaluate();
}
return lispExpression;
}

function lisp_function(the_function, arguments) {
return new LispExpression(
the_function,
arguments
);
}

This is all put together so we don't evaluate functions until they are 'called'. Remember that JS, just like most other algol derived languages will evaluate the inner most functions first - while lisp will evaluate the outermost.

Next we define some basic building blocks. Note that the 'control structures' are all just functions - after this we will be a little functional and a little lisp like.

function reduce(reducer, argument_list) {
return lisp_function(
function reduce() {
if(arguments.length > 0) {
var total = null;
reducer = lisp(reducer);
argument_list = lisp(argument_list);
for( var i=0; i<argument_list.length; i++) {
total = lisp(reducer(total, argument_list[i]));
}
return total;
}
return null;
},
arguments
);
}

function map(mapper, argument_list) {
return lisp_function(
function map() {
if(arguments.length > 1) {
var result = [];
for( var i=0; i<argument_list.length; i++) {
result.push( lisp(mapper(argument_list[i])) );
}
return result;
}
return null;
},
arguments
);
}

function if$() {
return lisp_function(
function(condition, trueExpression, falseExpression) {
if( lisp(condition) ) {
return lisp( trueExpression );
} else if(falseExpression) {
return lisp( falseExpression );
}
return null;
},
arguments);
}

function seq() {
return lisp_function(
function () {
var last_argument = arguments.length-1;
for( var i=0; i<last_argument; i++) {
lisp(arguments[i]);
}
return lisp(arguments[last_argument]);
},
arguments
);
}

obviously I should have cond and stuff in there, but whatever :) I do have map and reduce, for list goodness.

Some more building blocks follow. These don't have to use the lisp_function stuff anymore - that's all covered above - they can now comfortably be built up with the above plus simple javascript features (like adding...)

function add() {
return reduce(
function add(running_total, value){
return running_total == null ? lisp(value) : running_total + lisp(value);
},
arguments);
}

function sub() {
return reduce(
function add(running_total, value){
return running_total == null ? lisp(value) : running_total - lisp(value);
},
arguments);
}

function print() {
return reduce(
function print(running_total, value){
out.println(lisp(value));
return null;
},
arguments);
}

(out.println is provided by the Rhino runtime I am doing thsi work in) One would like to keep these JS polluted functions to a minimum, of course.

Finally some examples

function printAndReturn(aString, aValue) {
return seq(print(aString), aValue);
}

lisp(if$(false, print("got true"), print("got false")));
// got false
// null

function additup() {
return add(
1,
if$(
false,
printAndReturn("two", 2),
printAndReturn("three",3)),
4);
}

lisp(print(1,2,"fkfkfk"));
// 1
// 2
// fkfkfk

lisp( additup() );
// three
// 8

function add_one(a_value) {
return add(1, a_value);
}

lisp( map(add_one, [1,2,3]) );
// 2,3,4

lisp(
reduce(
if$(true, add, sub),
map( add_one, [1,2,3])
)
);
// 9

Well, I think it's neat :P