AjLisp en Javascript (Parte 3) Define, Lambda y Closures

Published on Author lopezLeave a comment

Anterior Post

Veamos la definición de nuevas formas en AjLisp, mi intérprete Lisp escrito en Javascript (github repository). Un forma especial clave en AjLisp es la define:

var defineForm = new SpecialForm();
defineForm.eval = function eval(list, env)
{
	var name = list.first().name();
	var value = list.rest().first();
	var body = list.rest().rest();
		
	if (isNil(body)) {
		value = evaluate(value, env);
	}
	else {
		value = new Closure(value, env, body);
	}		
		
	environment[name] = value;
	return value;
}

Se puede usar para definir datos simples globales. Mis primeros tests:

test("Evaluate Simple Expression with Atoms", function() {
	eval("(define one 1)");
	eval("(define two 2)");
	eval("(define three 3)");
	equal(eval("one"), 1);
	equal(eval("(quote one)").asString(), "one");
	equal(eval("(list one two three)").asString(), "(1 2 3)");
	equal(eval("(first (list one two three))"), 1);
	equal(eval("(rest (list one two three))").asString(), "(2 3)");
	equal(eval("(cons one (list two three))").asString(), "(1 2 3)");
});
		

Pero define está preparada también para recibir tres parámetros:

(define mapfirst (fn lst)
  (if (nilp lst)
    nil
    (cons
      (fn (first lst))
      (mapfirst fn (rest lst))
    )
  )
)

El primer parámetro es el nombre de la propiedad a definir en el ambiente (environment) global. El segundo parámetro es una lista de parámetros. El tercero es la forma a aplicar. Entonces, define se puede usar para definir nuevas funciones, formas. Es equivalente a

(define mapfirst (lambda (fn lst)
    (if (nilp lst)
      nil
      (cons
        (fn (first lst))
        (mapfirst fn (rest lst))
      )
    )
  )
)

Vean el lambda. Es una forma especial:

var lambdaForm = new SpecialForm();
lambdaForm.eval = function eval(list, env)
{
    var argnames = list.first();
    var body = list.rest();
    return new Closure(argnames, env, body);
}

Tanto define (con tres parámetros) como lambda terminan creando un Closure. La clausura recibe una lista de nombres de argumentos, un ambiente (closure environment) y un cuerpo. Entonces, cuando un closure se evalúa:

function Closure(argnames, closureenv, body) {
	body = new List(doForm, body);
	
	this.eval = function(args, environment) {
		var newenv = makeEnvironment(argnames, args, closureenv);
		return evaluate(body, newenv);
	};
}
	
Closure.prototype.evaluate = Form.prototype.evaluate;
Closure.prototype.apply = Form.prototype.apply;

La parta clave es el .eval: la evalucación de closure emplea un nuevo environment, basado NO EN EL ACTUAL, sino en el closureenv, el ambiente recibido en el constructor cuando la clausura fue creada. Esta es una típica implementación en un intérprete Lisp.

Próximos temas a discutir en posts: flambda, mlambda, evaluación de macros, y el parser.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *