About Me

Hello, I'm an eclectic software professional working with enterprise software at SAP. This blog only contains my personal views, thoughts and opinions. It is not endorsed by SAP nor does it constitute any official communication of SAP.

Thursday, April 14, 2011

Metaprogramming in Nemerle

Nemerle is a generally unknown .NET language that nevertheless has many impressive features. Its syntax is close to C#, but it has many functional programming constructs, like algebraic data types and pattern matching, that are not found in the Redmond language. It also has a very powerful metaprogramming system, where special constructs can participate in the compiler's execution pipeline and generate or manipulate code in the target program.

Metaprogramming can be very useful to cut on duplication, to apply consistent policies throughout a codebase and to raise the overall expressiveness of the code. It's one of the things that makes Ruby on Rails such a compelling and successful web application framework. Unlike Ruby, Nemerle is a compiled, statically typed language, where metaprogramming is possible already in the compilation step.

For a very simple (and unrealistic) example, imagine that we use lots of infinite loops in our code. In some parts we use while(true) ..., in other parts for(;;) ..., and still in others, do ... while(true);. None of these constructs reveal the true intent of our code, which is to loop forever (well, not really for this example, but I'm sure you can extrapolate this to some real obscure code pattern). In C#, we could create a method to encapsulate our intent:

public static class Forever {
  public static void Run(Func<Loop> action) {
    Loop control;
    do control = action(); while (control == Loop.Continue);
  }
}
public enum Loop { 
  Continue,
  Break
}

And use it like this:

int i = 0;
Forever.Run(() => {
  ++i;
  Console.WriteLine(i);
  if (i == 10) return Loop.Break;
  return Loop.Continue;
});

To simulate the statements break and continue, the Forever.Run method takes a function that returns an enumeration that indicates if the iteration should continue (Loop.Continue) or break (Loop.Break). Very cumbersome, isn't it? What's missing in C# is the ability to extend the language to create a more consistent experience. In Nemerle, you can use a macro to create code like this:

using System.Console; // WriteLine
using Nemerle.Imperative; // break
using MyMacros; // forever

...

mutable i = 0;
forever {
  ++i;
  WriteLine(i);
  when (i == 10) break;
}

The "keyword" forever is not part of the language. It's created by the following macro (which is compiled in a separate assembly):

namespace MyMacros {

  macro __Forever(body)
  syntax("forever", body) {
    <[
      while (true) $body;
    ]>
  }

}

This macro takes a body as a parameter (which can be any series of statements), introduces new syntax into the language (the "keyword" forever followed by the given body), and then simply issues an infinite loop using while(true) .... Code within <[ and ]> is transformed into an abstract syntax tree (AST) representation that the compiler then inserts into the program tree, at the place where the macro is invoked. This is called quasi-quotation, and also supports interpolation of values known at compile time, like $body, which is used to include the body parameter into the generated code.

Interestingly enough, most control statements in Nemerle are implemented as macros, like for, foreach, while, break, continue and using.

Nemerle macros are effectively compiler plugins, that work on the same structure (the AST) that the compiler uses. This is much more powerful than the simple text substitution macros in C/C++. It's equivalent to Lisp macros. They can also operate on more high level constructs, such as classes, and add or change members or introduce new types into the compilation unit.

More importantly, they can be an effective tool for a developer to create more expressive, clearer code.

No comments:

Post a Comment