Grok The rest - vilinski/nemerle GitHub Wiki

This page is a part of the Grokking Nemerle tutorial.

Table of Contents

Named parameters

This feature (in this form) comes from python. When you call a function you can name some of its parameters (which allows putting them in a different order than in the definition).

frobnicate (foo : int, do_qux : bool, 
                       do_baz : bool, 
                       do_bar : bool) : int
{
  // this is completely meaningless
  if (do_qux && !do_baz) foo * 2
  else if (do_bar) foo * 7
  else if (do_baz) foo * 13
  else 42
}

Main () : void
{
  // Parameters' names can be omitted.
  def res1 = frobnicate (7, true, false, true);
  
  // This is the intended usage -- the first 
  // (main) parameter comes without a name
  // and the following flags with names
  def res2 = frobnicate (7, do_qux = true,
                            do_baz = false, 
                            do_bar = true);

  // You can however name every parameter:
  def res3 = frobnicate (foo = 7, do_qux = true, 
                         do_baz = false, do_bar = true);

  // And permute them:
  def res3 = frobnicate (do_qux = true, do_bar = true, 
                         do_baz = false, foo = 7);

  // You can also omit names for any number of leading 
  // parameters and permute the trailing ones.
  def res2 = frobnicate (7, true,
                            do_bar = true,
                            do_baz = false);
  ()
}

The rules behind named parameters are simple:

  • named parameters must come after all unnamed (positional) parameters
  • positional parameters are assigned first
  • the remaining parameters are assigned based on their names
  • parameters with default values will be assigned automatically (except those that are already assigned)
Named parameters can be used only when names of parameters are known (which basically mean they do not work in conjunction with functional values).

The void literal

The void literal is written: ().

The void literal is quite a tricky thing, since it represents the only value of type void, which, judging from the name, should be, errr... void. In fact, in some other functional languages this type is called unit, but in Nemerle the name comes from System.Void and the void type is an alias for it.

In C# the void type is used as a return type of functions. It does not make much sense to use it elsewhere. It could be needed, however, if, for example, you want to use the Hashtable [a, b] type as a set representation of strings. You can use Hashtable [string, void] then. And this is the place to use the void value -- when you call a set method, you need to pass something as a value to set -- and you pass the void value.

You can also use the void value to return it from a function -- as you remember, the return value of function is the last expression in its body. However, in most cases the last expression will already have the right void type.

Operator overloading

You can overload existing operators or add new ones simply by specifying them as a static method in some type. The name of method should be some quoted (with @) operator. For example, the following class definition:

 class Operand {
   public val : int;
   public this (v : int) { val = v }

   public static @<-< (x : Operand, y : Operand) : Operand {
     Operand (x.val + y.val);
   }
 }

contains the <-< binary operator processing Operand objects. It can be used like:

 def x = Operand (2);
 def y = Operand (3);
 def z = x <-< y;
 assert (z.val == 5);

Unary operators can be created by giving only one parameter to the method.

Blocks

Blocks are a generic, structured replacement for the imperative break and continue statements. Consider the following example of prematurely returning from a function in C#:

bool AllGreaterThan(int[] numbers, int limit) {
  foreach (int number in numbers) {
    if (number <= limit)
      return false;
  }

  return true;
}

This works in C#, because the first return statement aborts execution of the entire function. In Nemerle, we have a similar control structure that is independent of functions:

AllGreaterThan: {
  foreach (number in numbers) {
    when (number <= limit) 
      AllGreaterThan(false);
  }

  true;
}

This looks similar to the C# example, but we are using the name of the block, AllGreaterThan, instead of return, to exit from the named block, instead of the entire function, passing the given value.

The Blocks article provides a more detailed description of blocks and their use case, and shows how to get C#-like continue, break and continue statements in Nemerle.

Macros

Nemerle has very powerful code-generating macros. They are more akin to Lisp macros than macros found in C preprocessor. We are not going to explain here how to write macros (if you are curious, please see macros tutorial), but will describe a few often used macros.

Core language constructs

First of all, if, while, for, foreach, when, using, lock, etc. are all macros.

When you write code like:

using (stream = System.IO.FileStream ("file.txt")) 
{
  lock (this) {
    ...
  }
}

it first gets transformed into

def stream = System.IO.FileStream ("file.txt", System.IO.FileMode.Open);
try {
  System.Threading.Monitor.Enter (this);
  try {
    ...
  } finally {
    System.Threading.Monitor.Exit (this)
  }
} finally {
  def disp = (stream : System.IDisposable);
  when (disp != null) {
    disp.Dispose ()
  }
}

The transformation is done by macros, which also introduce using and lock syntax to the language.

The greatest thing is that programmer is allowed to define his own macros, introducing his own syntax.

Design by contract

Design by contract macros allows you to specify assertions about your program. In other languages they are usually laying around in comments, while in Nemerle you can explicitly decorate code. This way contracts are instantly checked during program execution.

Other examples

Other examples of macros:

  • printf, sprintf -- uses a similar syntax to the same function in C, but is checked at the compile time
  • scanf -- likewise
  • print -- it does the $-expansion known from shell or perl
  • assert -- much like the C macro
You can see a page about macro usage for some other high level examples.
⚠️ **GitHub.com Fallback** ⚠️