Functional values en AjSharp

Published on Author lopezLeave a comment

Una de las características que quería tener desde el principio en mi intérprete AjSharp era que las funciones y rutinas sean ciudadanos de primera clase en el lenguaje (en este post uso la palabra clave function pero se puede usar también sub para definir una subrutina).

El código está en el proyecto de código de Google:

http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage

AjLanguage es el lenguaje núcleo (que no tiene ni parser ni lexer, sólo lo esencial del intérprete). AjSharp es el parser y el lexer implementado sobre el AjLanguage. De esta forma, podría implementar AjBasic u otros lenguajes similares, usando AjLanguage como base y núcleo.

En AjSharp, podemos definir funciones como en otros lenguajes:

function Square(n) { return n*n; }

Podemos definir una función anónimca y asignarla a una variable:

MySquare = function (n) { return n*n; };

Un functional value puede ser usado como parámetro:

function Apply(func,values)
{
  list = new List();
  
  foreach (value in values)
    list.Add(func(value));
    
  return list;
}
numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
function Square(n) { return n*n; }
squared = Apply(Square, numbers);
squared2 = Apply(function (n) { return n*n; }, numbers);

La función Apply definida arriba, toma una función, una lista, y retorna otra lista de elementos que resultan de evaluar f(e), donde f es la función recibida, aplicada a cada elemento e de la lista que se pasó como parámetro. Podemos invocar Apply con el nombre de la función (que en realidad es el nombre de la variable que contiene al functional value) o definidiendo la función directamente en la invocación.

Estoy pensando sobre algunas alternativas de implementación del alcance y visibilidad de variables en expresiones funcionales (dará para otro post el tema). Si definimos una función, AjLanguaje (el núcleo de AjSharp) usa una closure (clausura), así que cuando la función es invocada, el binding environment original es usado. En los ejemplos de más abajo se usa esa característica.

Cada función implementa:

    public interface ICallable
    {
        int Arity { get; }
        IBindingEnvironment Environment { get; }
        object Invoke(IBindingEnvironment environment, object[] arguments);
        object Invoke(object[] arguments);
    }

esto es, cuando es invocada, recibe un binding environment (un diccionario que dado el nombre de una variable nos da su valor asociado). Un funcional value no usa ese binding environment recibido, y lo reemplaza por el environment original que tenía en el momento de haber sido definida, creada.

Podemos retornar una función como retorno de una función:

function MakeIncrement(x) 
{
  return function(n) { return n + x; }; // x is bounded to local parameter
}
Increment2 = MakeIncrement(2);
result = Increment2(2);
// result == 4
Increment3 = MakeIncrement(3);
result2 = Increment3(2);
// result2 == 5
result3 = MakeIncrement(4)(3);
// result3 == 7
x = 4;
result4 = function(n) { return n+x; }(5);
// result4 == 9

MakeIncrement retorna una función que usa y accede a la variable x, ligada en el binding environment de llamada.

En el último comando, la función es definida e invocada en el mismo comando.

AjSharp tiene clases, y podemos definir funciones y rutinas como métodos:

class Person
{
  var Name;
  var Age;
  
  function AddYears(years) 
  {
    this.Age = this.Age + years;
  }
}

Pero también podemos agregar funciones en cualquier momento:

class Person
{
  var Name;
  var Age;
  
  function Person()
  {
    x = 100;
    this.GetYears = function () { return this.Age; };
    this.GetX = function() { return x; };
  }
}
adam = new Person() { Name = "Adam", Age = 800 };
result = adam.GetYears();
// result == 800
result2 = adam.GetX();
// result2 == 100

o agregar funciones/rutinas directamente a objetos:

adam.AddYears = sub(n) { this.Age = this.Age + n; };

Hace unas semanas, leí el post:

Functional Programming in Javascript

de James Carr, así que decidí probar las capacidades de AjSharp para manejar functional values adaptando algunos de esos ejemplos, como:

function runningSum(start){  
  sum = start;  // you could use var sum, it's local
  
  return function(a){  
    sum = sum + a;  // function access the "outer" sum
    return sum;
  };
}  
sum = runningSum(3);  // makes function
result = sum(2); // returns 5  
result2 = sum(10); // returns 15  

Podemos usar directamente el parámetro como acumulador:

function runningSum(start){  
  return function(a){  
    start = start + a;  // function access the "outer" start
    return start;
  };
}  
sum = runningSum(3);  // makes function
result = sum(2); // returns 5  
result2 = sum(10); // returns 15  

Agregué soporto para aplicar una función como si fuera un método a un objeto, como en uno de los ejemplos de  Carr (este tipo de llamada es soportada en Javascript que tiene la clase Function):

person = new { Name = "Adam", Age = 800 };
GetAdjustedAge = function (x) { return x + this.Age; };
result = GetAdjustedAge.Call(person, 10); // result == 810

Próximos pasos: estabilizar el alcance de las varaibles y el acceso al environment global. Por ahora, me divertí bastante implementando valores funcionales

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 *