Record macro - vilinski/nemerle GitHub Wiki

Basic concept

Record macro is very useful for generating simple constructors in classes. In many designs tt is quite common practice to create small utility data-structures, which consists of several fields. In order to simplify their creation it is necessary to write constructors, which takes initial values of fields in parameters and assigns them to actual fields in a class. So we often end up with following dummy code:

class Person {
  public name : string;
  public age : int;
  public sex : bool;

  public this (name : string, age : int, sex : bool) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
}

But wait, what a waste of time... This pattern is so common that often language designers add specialized rules, which allow initalizing fiels without writing such constructors.

In Nemerle we can just use macros, to make them automatically generated. So the above code can be simply rewritten as:

[Record]
class Person {
  public name : string;
  public age : int;
  public sex : bool;
}

and that is all, what is needed. Record is a macro, which checks what fields are defined inside the class and generates constructor to initialize them.

Inheritance

Things gets a little bit more complex when we want to make use of our Record utility in classes within some object-hierarchy. In general the question is, which constructor from base class should we call.

After some problem inspection we conclude that in most cases we just want constructor in derived class, which would take and pass all parameters needed by base constructor plus parameters initializing freshly introduced fields.

So for example:

[Record]
class Animal {
  public name : string
}

[Record]
class Cat : Animal {
  public fur : System.Drawing.Color;
}

will be transformed to following code:

class Animal {
  public name : string;

  public this (name : string) {
    this.name = name;
  }
}
class Cat : Animal {
  public this (name : string, fur : System.Drawing.Color) {
    base (name);
    this.fur = fur;
  }
}

So the rule is as following:

  • take all accessible (public, protected, internal) constructors from base class
  • take all instance fields declared directly in current class
  • for each of base constructors, create constructor in current class, which will have new parameters appended to the end of original constructor's parameters list

Filtering fields

Sometimes we do not want all fields to become subject of our constructor generation scheme. It is often that we want to just have constructor for 3-4 fields or that we want all except one.

In order to allow flexible usage of Record macro, we added two optional parameters to Record', Include and Exclude.

Let us see them in action:

[Record (Include = [name, age, sex])]
class Person {
  public name : string;
  public age : int;
  public sex : bool;
  current_cash : decimal;
}

and

[Record (Exclude = [current_cash])]

will result in generating constructor only for first three fields.

You can use following values for Include and Exclude:

  • explicit enumeration of fields in []
  • regular expression in "", like Record (Include = "i.*"), matches all field beginning with 'i' letter
Of course you can use Include and Exclude at the same time.
⚠️ **GitHub.com Fallback** ⚠️