Prefix - Anonymous Functions and Lambdas

Monday, September 26th, 2011     #javascript #everything

A day or two ago I wrote about Prefix' support for delegates types - which are great, but they're only half the story. To make the most of them, you really need anonymous functions and lambda expressions.

This post is about Prefix - a modern language that compiles to JavaScript. A language where C# is the inspiration - not the goal. Read more on the Prefix Project Page.

A delegate type lets you declare the required prototype of function and pass references to matching functions around. What we need now is an easier way to declare these functions inline - without having to create an actual "function" per se.

Anonymous Functions

An anonymous function is a function declared without a name and with an inferred return type. It's declared using the delegate keyword as part of an expression. Consider the following where an unnamed ("anonymous") function is passed to a another function:

{{C#}}
extern dynamic Console;

void DoSomething(Func<int, int> cb)
{
   Console.WriteLine(cb(10));
}

DoSomething(delegate(int x) { return x*88; });
{{JavaScript}}
(function() {

function DoSomething(cb)
{
    Console.WriteLine(cb(10));
};

// Start Global Code
DoSomething(function(x) {
   return x*88;
});

})();

Because anonymous functions don't declare a return type, the compiler must be able to infer it from usage. (in the above example it's infered from DoSomething's cb parameter which is declared as Func<int, int>

Lambda Expressions

Lambda expressions are an even more concise way of writing an anonymous function that directly returns the result of a single expression. The syntax for lambda expressions is based around the => operator and prefix supports the same syntax formats as C#. eg:

  • ()=>99 - a lambda expression with no parameters
  • x=>x*2 - a lambda expression with a single untyped parameter
  • (x,y)=>x*y - a lambda expression with multiple untyped parameters
  • (int x, int y)=>x*y - a lambda expression with multiple typed parameters

A lambda expression is equivalent to a anonymous function that simply returns a value. So this:

(int x, int y) => x * y

is equivalent to:

delegate(int x, int y) { return x * y; }

Note too that with lambda expressions (unlike anonymous functions) you don't have to specify type arguments for the parameters - they're inferred in the same way the return type is.

Solving the this/that Conundrum

In JavaScript when nesting one function inside another, the inner function doesn't inherit the this reference from the outer function. In order to reference the outer class the developer must manually setup a that reference in the enclosing closure. (see here for a better explanation).

The Prefix compiler however has enough type information to know how to set this up automatically. So if an anonymous function inside a class method refers to a member variable of the outer class, this is automatically stored in a local variable $this and the nested function automatically uses it.

Here's an example showing both a lambda expression and the use of $this:

{{C#}}
extern dynamic Console;

class MyClass
{
 public int Value;
   public void DoSomething(int input)
  {
       Func<int, int> fn = x=>Value * x;
       Console.WriteLine(fn(input));
   }
}

var x = new MyClass();
x.Value = 23;
x.DoSomething(10);
{{JavaScript}}
(function() {

// class MyClass
var MyClass = function() {};
MyClass.typeName = 'MyClass';
MyClass.prototype.Value = 0;

MyClass.prototype.DoSomething = function(input)
{
    var $this = this;
   var fn=function(x) { return $this.Value*x; };
   Console.WriteLine(fn(input));
};

// Start Global Code
var x=new MyClass();
x.Value = 23;
x.DoSomething(10);

})();

Generic Type Inference

All that's left to do in this area is type inference of generic function arguments from lambda's and anonymous functions - coming soon!

« Older - Prefix - ForEach Statements Newer - Prefix - typeof and typename operators »