Nemerle for OOP Programmers Week 1 - vilinski/nemerle GitHub Wiki

During this lesson we will learn how to define good ol' classes in Nemerle. It works much like in C# (or Java, C++) but there are some differences.

We also take a look at loops and a few basic macros.

This week still doesn't introduce any functional features. We'll find out about them next week.

Table of Contents

Class definitions

We assume you know what a class is.

Classes in Nemerle are defined with the class keyword. The basic form is:

class MyClass {
  // something inside
}

There are several possible entities that can be defined inside classes. All of them can be proceeded by access modifiers, and some by other modifiers.

Modifiers

There are several kinds of modifiers. Note that you're probably only going to use public, static and mutable at first.

We will first discuss access modifiers. Modifiers specify the kind of access other classes have to given entity. Access attributes are valid on all kinds of entities defined in classes. Everything is private by default. The public and internal modifiers are also valid on top-level classes (which are internal by default).

  • public - everyone can access
  • internal - only classes in the current assembly (DLL or EXE) can access
  • private - only current type can access
  • protected - access is limited to current type and its derived types
  • protected internal - means protected or internal - access limited to derived types or types in the current assembly
Next are the modifiers changing the semantics of a given entity:
  • static - for fields, it means that there is only one instance of this field shared by all instances of the class. For methods (and properties) it means the implicit this argument is not passed, therefore the method can only use static fields and methods directly (instance methods/fields can be used, provided they are called on a specific object)
  • mutable - is valid only for fields and means that a field can be updated outside the constructor. There is no corresponding modifier in C# or Java; everything is mutable by default there. C# does have the readonly modifier to force fields to be non-mutable.
  • volatile - valid only for fields, it means that the field has to be always read from the memory, and written instantly (it avoids any caching in the JIT-ed code). It's mainly useful for multithreaded programs.
  • extern - used on methods, with DllImport attributes to call out to native code. We probably won't cover this topic in this course. You can google on PInvoke for more information.
  • partial - valid only on type definitions, it means that a given type definition is split across several files. It works as if you defined several classes with the same name (and all with the partial modifier), but the compiler merges them together.
Next are the modifiers used to control virtual methods and name hiding. We only explain new here. The others are described in the section about virtual calls.
  • new - valid on all entities (including nested types), it means to ignore a member with the same name in super class. Otherwise using the same name results in a warning.
  • virtual
  • override
  • abstract
  • sealed

Side note: mutability

One way to think about the mutable modifier is that it's much like default-private fields. That is, you need to make a conscious decision to make a field mutable, just as you need a conscious decision to make it public. Therefore fields will end up non-mutable only when they need to. As you continue programming in Nemerle, you will find yourself not needing that much mutability.

Accessing instance vs. static members

Values created from classes are known as instances of the class. Classes work like models, and instances are real representations of that model in memory. If you want to access a instance method, that is, a method not declared static, you first have to create an instance of the class using a constructor (see below). Then, you use the . operator to "dive into" the class members: properties, fields, methods...

On the other hand, static members can only be accessed through the name of the class. Trying to call a static method from an instance will always fail in Nemerle. Some other languages allow both uses, so be careful!

Custom attributes

This is rather advanced topic, but this syntax is reused by top-level macros, so we briefly mention it here.

In .NET all metadata elements (classes, methods, fields and so on) can have a special, additional information attached. This information is (in most cases) not interpreted in any way by the runtime, therefore it is good place for placing documentation, authorship/copyright tags, and so on. This information can be accessed by user applications at runtime.

The syntax for attaching this information in Nemerle (and in C# for that matter) is:

[MyCustomAttribute (7, "foobar")]
public class FooBar {
  [OtherCustomAttribute]
  SomeMethod () : void
  {
  }
}

Custom attributes are classes subtyping System.Attribute, so the user can define new custom attributes, though some are provided by the base class library.

If you're interested in this mechanism, it may be a good idea to consult MSDN attributes tutorial, after going through this course. It's about C#, but, except for minor syntactic differences it also applies to Nemerle.

In Nemerle, certain custom attributes trigger execution of macros - that is special compiler extensions. Later this week we will learn how to automatically create constructors and accessors.

Methods

Methods are used for grouping code together for later reuse. We've already seen how to define methods in the previous week.

As you recall, method calls are written using the method name, and then writing the parameters between parenthesis.

Fields

There is nothing special about fields in Nemerle (compared to fields in C#/Java/C++), except for their immutability. If you do not declare a field to be mutable, they can be only set inside the class or instance constructor (depending on if the field is static or not). This is much like the readonly modifier in C# or final in Java.

You can also initialize fields at the place of their definition, which causes the appropriate initialization code to be added to the class or instance constructor.

Fields are defined with the following syntax:

modifiers name : type;

or with the initializer:

modifiers name : type = expression;

Modifiers are optional, and fields are private by default. If you omit the initializer fields have the value of 0, 0.0 or null for reference types.

In fact, even if you supply the initializer you can still observe this default value, for example:

class Test {
  static public x : int = y + 1;
  static public y : int = x + 2;
}
System.Console.WriteLine (Test.x);
System.Console.WriteLine (Test.y);

will print 1 and 3, because when initializing x, y still has its default value of 0. As you can see, the initializations are performed top-down.

Instance constructors

Unlike in C#/Java/C++, the constructor has a fixed name, it's called this.

Example:

class Point {
  public x : float;
  public y : float;
  public this (ini_x : float, ini_y : float)
  {
    x = ini_x;
    y = ini_y;
  }
}

Instead of inventing this ini_ stuff one can also use this pointer directly:

class Point {
  public x : float;
  public y : float;
  public this (x : float, y : float)
  {
    this.x = x;
    this.y = y;
  }
}

The constructor is later called by the class name:

def the_point = Point (3.14, -3.14);

There is no new keyword, as is present in C# or Java.

The [Record] macro

If all your constructor is going to do is assign field values, which will all supplied by the user, you can use the [Record] macro. So the example above can be reduced to:

[Record]
class Point {
  public x : float;
  public y : float;
}

This useful especially for creating small helper classes.

Default constructor

If you do not provide an instance constructor at all, the compiler adds a default, public (protected, for abstract classes), parameter-less, empty one. Therefore the following piece of code is valid:

class MyClass {} 
def c = MyClass ();

Example

Now, now that we know about fields, methods and constructors, we can start some examples. We will try to program a robot called Marvin.

class Marvin {
  // Marvin starts with a fairly positive attitude
  mutable attitude : int = 100;
  mutable x : int;
  mutable y : int;
  mutable direction : int;
  
  public this (ini_x : int, ini_y : int)
  {
    x = ini_x;
    y = ini_y;
    // head north
    direction = 0;
  }
}

This example is completely equivalent to:

class Marvin {
  mutable attitude : int; // CHANGED
  mutable x : int;
  mutable y : int;
  mutable direction : int;
  
  public this (ini_x : int, ini_y : int)
  {
    attitude = 100; // CHANGED
    x = ini_x;
    y = ini_y;
    // CHANGED
  }
}

As you can see we moved initialization of attitude to the constructor. This does exactly the same thing that the field initializer does. We also removed direction = 0, because 0 is the default value for int anyway.

A little prize: string interpolation

Now we can add some more complicated behavior to Marvin, we start with a Turn method:

public Turn (delta : int) : void
{
  // we need to protect our engine, Marvin cannot
  // turn too much at once!
  if (delta < -90 || delta > 90)
    System.Console.WriteLine ($ "Cannot turn that much ($delta degrees)!");
  else {
    // having to turn makes our robot feel worse
    attitude -= 5;
    direction += delta;
    // call the robot's firmware here
  }
}

(Because of our NDA with Sirius Cybernetics, we cannot reveal how to call Marvin's firmware to actually make it move, therefore we will just skip this :-)

The interesting part is the usage of the $ macro. It takes a string as an argument, and interprets the dollar signs in it in a special way -- it evaluates them as expressions, calls ToString() on them and puts the results into the string. If you need to place a more complicated expression after $, you need to use parens, like in:

System.Console.WriteLine ($ "Cannot turn that much ($(delta / (180 * 3.1415)) radians)!");

This works mostly like in PHP, perl or Bourne Shell, but it is checked at the compile time, so if you put an undefined variable you will get a compile time error, not empty string at runtime (check it out!).

The Framework provides also another way to interpolate strings. Inside the string, you can include {n} directives, and they will be changed with the "n + 2"-th parameter. Console.WriteLine is one of the methods that allow this. In other places, one can use:

def addition = string.Format ("{0} + {1} = {2}", n1, n2, n1 + n2);

As you can imagine, it is easy to mess up matching the numbers, so better just stick with $ :-)

Some loops

Now we can add another method:

public TurnBack () : void
{
  for (mutable i = 0; i < 2; ++i)
    Turn (90);
}

The for loop works much like other C-like languages -- the first expression is evaluated before the loop, the second is the condition and the last one is the increment.

So what about a general turning method without limits?

public TurnAny (mutable delta : int) : void
{
  while (delta != 0)
    if (delta > 90) {
      Turn (90);
      delta -= 90;
    } else if (delta > 0) {
      Turn (delta);
      delta = 0;
    } else if (delta < -90) {
      Turn (-90);
      delta += 90;
    } else {
      Turn (delta);
      delta = 0;
    }
}

As you can see, we have a while loop too. Moreover, in order to assign values to a parameter, we need to mark it mutable.

More about classes!

Static constructors

A static constructor is executed once for the program (or more exactly, AppDomain) lifetime, when the class is first referenced. It's a good place to include some class-wide initialization code.

Initializers specified for static fields are added to the static constructor.

Static constructors need to be private, parameter-less and ergh... static. For example:

using System.Console;

class Hello {
  static this ()
  {
    WriteLine ("static ctor");
  }

  static public Run () : void
  {
    WriteLine ("Run");
  }
}

WriteLine ("hello");
Hello.Run ();
WriteLine ("bye");

/* Produces:
hello
static ctor
Run
bye
*/

Note how "hello" is written before static ctor is run, but before the Run method.

Properties

Properties provide language level support for the get/set accessor design pattern commonly found in Java and C++. Most .NET languages (including C#) provide properties. A property called Foo of type int is internally a set of two methods get_Foo() : int and set_Foo (value : int). However instead of writing:

def my_value = some_object.get_Foo ();
some_object.set_Foo (42);

we write:

def my_value = some_object.Foo;
some_object.Foo = 42;

As you can see this is only syntactic sugar. There is also a special syntax for defining properties:

using System.Console;

class MyClass {
  mutable m_foo : int;
  
  public Foo : int
  {
    get { 
      WriteLine ("get_Foo called");
      m_foo
    }
    set {
      when (value > 3)
        WriteLine ("value bigger than three");
      m_foo = value;
    }
  }
}

def c = MyClass ();
c.Foo = 1;
WriteLine ($ "c.Foo = $(c.Foo)");
c.Foo += 42;
WriteLine ($ "c.Foo = $(c.Foo)");

/* Output:
get_Foo called
c.Foo = 1
get_Foo called
value bigger than three
get_Foo called
c.Foo = 43
*/

Note how += is changed to c.Foo = c.Foo + 42, which results in call to get_Foo before setting the value.

Also note, that the set method has implicit parameter called value.

You can omit get or set, but not both. If you omit set, trying to write to the property will result in an error. Omitting get means you can't read the property.

The [Accessor] macro

If your property is only going to return the value of a field, you can use the [Accessor] macro. It sits in Nemerle.Utility. You place it on a field. If you don't supply any parameters, it generates a public getter:

using Nemerle.Utility;

class MyClass {
  [Accessor]
  foo_bar : string = "qux";
}

def c = MyClass ();
System.Console.WriteLine (c.FooBar);

/* Output: qux */

You can also supply another name for the accessor, request a setter, or make them internal. Please refer to accessor macros page for details.

Inheritance and virtual calls

Inheritance is one of the central concepts of OOP. It is a means of extending the functionality of an existing class. A class can inherit all the behavior of some original class, then extend it by adding new methods, fields and/or properties.

For example we can define a class called Robot and later define two classes inheriting from it: Marvin and R2D2. Robot is then said to be supertype of Marvin and R2D2, and Marvin and R2D2 are subtypes of Robot.

The inheriting class can also modify the behavior of the original class. This is done through virtual methods. For example, Robot can define a virtual SelfDestruct method, which R2D2 can override to refuse self-destruction, while Marvin can reuse the method from Robot (by just doing nothing).

The syntax is:

class Robot {
  public virtual SelfDestruct () : void
  {
    // call robot firmware here
  }
}

class R2D2 : Robot {
  public override SelfDestruct () : void
  { 
    System.Console.WriteLine ("refusing");
  }
}

class Marvin : Robot {
 // don't override anything
}

As you can see, the colon (:) is used to mark the class to inherit (Java uses extends keyword here). Methods need to marked virtual, otherwise they cannot be overridden (unlike in Java). Additionally, the overriding method needs to be marked override (unlike in C++).

Abstract methods

Methods can also be marked abstract. You mustn't provide any body in this case. It means the method has to be eventually overridden by an inheriting class. A class is marked abstract when it has one or more abstract methods, either defined in the class itself, or an abstract method defined in the super type that is not overridden. One cannot construct instances of abstract classes.

Example:

abstract class Robot {
  abstract SelfDestruct () : void;
}

class R2D2 : Robot {
  public override SelfDestruct () : void
  { 
    System.Console.WriteLine ("refusing");
  }
}

class Marvin : Robot {
  public override SelfDestruct () : void
  { 
    System.Console.WriteLine ("self destructing in 5 seconds...");
  }
}

Modules

Another common pattern found in some C# classes, like Console, is to provide neither public nor default instance constructors, and make all members static. This prevents every type of instantiation, and works very well for resources where it doesn't make sense to have more than one object at once (like console, file system in UNIX/Linux systems...).

In Nemerle, this pattern has a special construct: modules. A module is declared exactly like a class, but every method or field you declared on has an automagically added static modifier. For example:

module Operations {
  public Add (n1 : int, n2 : int) : int {
    n1 + n2
  }

  public Substract (n1 : int, n2 : int) : int {
    n1 - n2
  }
}

// Note that module members are accessed through class name
System.Console.WriteLine (Operations.Add (1, 2));

Type conversions

Sometimes you need to convert an object from one type to other. You might want to convert an int to a double, or a class to its parent. In Nemerle, there are two conversion operators (or casting, as you C# guys like to call them), depending on whether the cast is checked at compile or runtime:

  • Type cast ( :> ): this operator checks the type at runtime. It is especially useful when you receive a parent class instance from a method or property and you want to cast it to a derived class. However, as classes can have more than one child class, the conversion cannot be checked on compile time, and may throw a InvalidCastException at runtime.
  • Type enforcement ( : ): you can only use this operator if the check is known to succeed at compile time. For example, conversion from short to int, or from one class to its parent.
Both operators are written the same way. First, the object you want to convert, then the operator, and finally, the result type.
def memoryStream = System.IO.MemoryStream ();
def stream = memoryStream : System.IO.Stream; // MemoryStream is a subclass of Stream

def n1 = int.Parse System.Console.ReadLine());
def n2 = int.Parse System.Console.ReadLine());
def resultAsShort = (n1 + n2) :> short; // We don't know if n1 + n2 fits on short, so we use :>

Exercises

Exercise 1

Create a Person class. This class needs to have FirstName, LastName and Telephone properties, as well as a BirthDate. BirthDate should be a record Date with the necessary fields. Additionally, add an Age property that computes the age of the person using the system current date. This property must not be writable.

Exercise 2

Play a little with inheritance and virtual calls. The traditional example is to create an Animal class with Legs property and a virtual Speak method. Then, create some more classes, like Dog, Cat..., overriding the Speak method. Watch the results!

Exercise 3

Take this small example and add ImperialTrooper subclass to it. DarthVader should have a radio connection (a reference) to imperial trooper, so he can ask (with a method) if he can see Luke.

Extra Exercise

Find out a little about XML Serialization (check MSDN, or Monodoc in the System.Xml.Serialization namespace), and write a method for opening and saving a contact.

ToC | Week 2

⚠️ **GitHub.com Fallback** ⚠️