Tuesday, March 4, 2008

Macro- and Template- like parametrization of JavaScript functions

Following example present some approaches that can be used to implement JavaScript constructs, that can mimic C++ templates, C #define, and Lisp macro's.

However, JavaScript syntax is still used in the examples. It is no such hacks of syntax in javascript as defmacro in lisp, and operator redefinition as in C++, so further used functional forms, with passing statements for execution as strings. Last example presents definition of control statement in terms of JavaScript object system.

This way, the basis for implementation, is the usage of eval function, that operates lexical contest, and JavaScript OOP.

For example,specific adder function can be built, that converts arguments in required way before made addition. We are adding arguments as strings and as integers in following example.

The JavaScript code is:

a = 5;

document.write("<br>"+ a + "<br>");

function parametrized_adder(param1,param2){
return function(a, b){
return eval( param1 +"(a) +" + param2 + "(b)");
};
};

var num_adder = parametrized_adder("parseInt","parseInt");

document.writeln("<br> int 5 + int 5 = " + num_adder(5,5) );
document.writeln("<br> str 5 + int 5 = " + (parametrized_adder("String","parseInt"))(5,5) );

And the result is:
5

int 5 + int 5 = 10
str 5 + int 5 = 55


The next example demonstrates workaround for argument passing by value. It is example of incf() function used for increasing value of its parameter by 1. In some tutorials on lisp incf, presented as first example of macro, because passing parameter in lisp is by value too.
Instead of using macro-way substitution we would use javascript object, that's values are mutable even if object passed to function.

var c = {value:0};
c.incf = function incf(para)
{
this.value++;
};

c.value = 5;
c.incf();

document.writeln("incf(5)=" + c.value);


result: incf(5)=6

Also, using the first presented technique, it is possible to define our own Lisp cond function equivalent, as it can be made by scheme and common lisp macro's. This function split arguments by pairs ; evaluate first; if it is true => evaluate second part.

function cond()
{
var items = cond.arguments.length
for (i = 0;i < items;i+=2)
{
if(eval(cond.arguments[i])) { eval(cond.arguments[i+1]);return true}
}
return false;
}

a = c // use previously defined c as prototype
b = c
a.value = 10;
b.value = 12;

cond ("a.value+b.value < 15", "a.value = 15; alert (\"a set to 15\")",
"a.value+b.value > 50", "b.value = 50; alert (\"b set to 50\")",
"a.value=7; b.value=7;true;","alert(\"both set to 7;\")");

document.writeln("final b=",b.value);
document.writeln("final a=",a.value);


result will be
final b=7 final a=7
and alert will be displayed about this setting.
also we can implement this example using object structure of javascript:


o_cond={body:[]};

o_cond.execute= function () {
for(i=0;i<this.body.length;i+=2)
{
if (eval(this.body[i])) {
eval(this.body[i + 1]);
return true;
};
};
};

o_cond.body = new Array ("a.value+b.value < 15" , "a.value = 15; alert (\"a set to 15\")",
"a.value+b.value > 50" , "b.value = 50; alert (\"b set to 50\")",
"a.value=7; b.value=7;true;","alert(\"both set to 7;\")");

o_cond.execute();


If previous values of a and b where 7 and 7 as set by previous example, than alert will be displayed that a set to 15; that indicates, that this approach(OOP), works for building lisp cond statement equivalent also.

firefox 2.0.12 under linux i686 was used for examples demonstrated.

No comments: