Skip to content

Quick guide

caseymarquis edited this page Jul 23, 2012 · 15 revisions

This is a quick guide covering nearly all of Nemerle's features. It should be especially useful to anyone who is already familiar with C# or a similar language:

Table of Contents

Variables

  • mutable a = 5;: mutable variables can be changed later
  • def b = 4;: normal variables cannot be changed
The type of the above variables is inferred by the compiler. This is the norm, rather than the exception.

Variables can be manually assigned types as follows: def c : float = 4;

  • def f' = f.getDerivative();: variable and type names using and starting with " ' " are valid in Nemerle

Operators

  • a = b;: assignment. This operator doesn't return a value, so a = b = c is not allowed
    • Compound assignment is allowed: a += b;
  • +: addition and string/list concatenation
  • -, *, /, %: subtraction/multiplication/division/modulo
  • a <-> b;: swap (exchanges the values of a and b)
  • ++, --: adds/subtracts 1 to/from a value

Logical Operators

  • ==, !=: equal/not equal
  • x.Equals(y): compares any two objects. Equals() checks by reference for reference types and by value for value types (including enums) by default. Types are allowed to provide their own override of the Equals method, and x and y may have different types.
  • >, <;, >=, <=: greater than/less than/greater than or equal to/less than or equal to
  • &&: logical AND
  • ||: logical OR
  • !: not
You can also use and/or/not by opening the Nemerle.English namespace

Bit Operators

  • &, |, ^: bit-level and/or/xor operations
  • %&&, %||, %^^: bit-level and/or/xor, returning true if the result is non-zero
  • >>, <<: right/left bit-shifting

Type Casts/Conversions

The implicit cast operator is used if a cast cannot fail (ie when casting to a supertype):

def x : int = 12; //Declaring a variable.
def y : long = x : long; //Casting it to something else.

The explicit cast operator is used if a cast may fail:

def x : int = 12; //Declaring a variable.
def y : char = x :> char; //Casting it to something else.

If an implicit cast is possible, the compiler will infer it and the use of the operator is not required.

$-Notation

$-Notation is the simplest way of inserting variables' values into strings in Nemerle.

  • def s = $"The number is $i";: replaces $i with the string value of i
  • def s = $"$x + $y = $(x+y)";: replaces $(...) with the string value of whatever is inside the parentheses. Useful for accessing members, performing calculations, and placing text immediately next to variables

Functions

Functions in Nemerle are declared either locally or as methods and return the value of their final statement. Regardless of how they are declared, they will generally look as follows:

add(a : int, b : int) : int {
  a + b; //This function returns a + b.
}

Methods

When declared as a method, a function's return and parameter types must be explicitly stated:

class Greeter{
  public Greet (name : string) : void { //Note the explicit types.
    System.Console.Write($"Hello $(name)!");
  }
}

Local Functions

Functions can be declared locally within methods and other functions. When declared locally, parameter and return types can be inferred.

class Greeter{
  public GreetTwice (name : string) : void { //A function declared as a method. Types must be explicitly stated.
    def sayHello(name) { //The types of this function are inferred
      System.Console.Write($"Hello $(name)!");
    }

    sayHello(name);
    sayHello(name);
  }
}

Function Types

Functions have types in Nemerle. Function types take the form of:

parameterTypes -> returnType

The type of the function

doSomething(i : int, c : char, s : string) : long {
  //Or don't do something.
}
is:
int*char*string -> long

Note that parameter types use the same notation as tuple types.

Functions as Parameters

Functions may use other functions as parameters:

def compute(f : int*int -> int, x : int, y : int){
  f(x,y)
}

def add(a, b){a + b}
def mul(a, b){a * b}

compute(add,2,3); //Returns 5
compute(mul,2,3); // Returns 6

Anonymous Functions and Partial Applications

All anonymous functions can be created using two different syntaxes:

  • compute ( fun (x, y) { x + y }, 3, 4): The fun keyword.
  • compute ( (x, y) => x + y, 3, 4): This alternate form.
The following syntax can be used for single variable functions:
def addFive = _ + 5;
addFive(10); // returns 15

It can also be used to access members:

def getName = _.name;
getName(hasName); //returns hasName.name

A partial application replaces one or more parameters of a function with some known value and yields another function with fewer parameters:

def addFive = compute (fun (x, y) { x + y }, _, 5);
addFive(6); // returns 11

Parameter Modifiers

Methods (but not local functions) may be passed arguments by reference using the 'ref' and 'out' modifiers.

Values passed using 'ref' must be initialized first:

public module Mod{
  public static Main(): void{
    mutable a = 3141, b = 2718;
    Mod.swap(ref a, ref b);
    System.Console.WriteLine($"a == $a and b == $b"); //Prints "a == 2718 and b == 3141"
  }
  
  public swap(a : ref int, b : ref int) : void{
    def temp = a;
    a = b;
    b = temp;
  }
}

Values passed using 'out' do not need to be initialized first:

public module Mod{
  public static Main(): void{
    mutable a, b;
    Mod.init(out a, out b);
    System.Console.WriteLine($"a == $a and b == $b"); //Prints "a == 2718 and b == 3141"
  }
  
  public init(a : out int, b : out int) : void{
    a = 2718;
    b = 3141;
  }
}

Default Parameter Values/Passing Arguments By Name

Parameters can be given default values, and arguments may be passed to functions using parameters' names:

def add(a = 4,b = 5){
  a + b;
}

add() //returns 9
add(10) //returns 15
add(10,10) //returns 20
add(b = 20) //returns 24
add(b = 20, a = 30) //returns 50

Classes and Modules

  • class Example { ... }: creates a class Example
  • module Example { ... }: creates a module Example. A module is a class in which all members are static.

Access Modifiers

  • public: can be accessed from anywhere.
  • private: can only be accessed by the class itself.
  • protected: can be accessed by the class itself and child classes.
  • internal: public within the assembly (DLL or EXE) where it was defined, and inaccessible elsewhere.
  • protected internal: public within the assembly (DLL or EXE) where it was defined, and protected elsewhere.

Other Modifiers

For Methods and Fields

  • static: Only a single instance exists. This instance is accessed statically: ClassName.StaticMember()
Fields Only:
  • mutable: Makes a field modifiable. Fields are immutable (read-only) by default.
  • volatile: Specifies that the field must always be read-from/written-to memory. Useful for multithreaded programs.
Methods Only:
  • extern: Used with DllImportAttribute to call native code.
Other:
  • partial: Only used with type definitions. Indicates a given type definition is split across several files.

Constructors

Constructors look like methods but use this for their name:

class Example {
  mutable Name : string;
  public this (name : string) {
    Name = name;
  }
}
  • def n = Example ("serras");: creates a new instance. No 'new' keyword is required.

Static Constructors

Executed once per type. No parameters.

class Example {
  static public this() { ... }
}

[Record]

Generates a constructor which assigns a value to every field:

[Record] class Point {
  public x : int; public y : int;
}
is the same as:
class Point {
  public x : int; public y : int;
  public this (x : int, y : int) {
    this.x = x; this.y = y
  }
}

Inheritance

  • class Human : Mammal { ... }: class Human inherits from Mammal or implements the interface Mammal

Inheritance Modifiers

  • abstract: The method contains no actual implementation, and must be overridden by a child class.
  • virtual: The method may be overridden by child classes.
  • override: Indicates that the method redefines an abstract or virtual method.
  • sealed: Used in conjunction with override. Indicates that no further derivation or redefinition is possible.
  • new: Allows name redefinition in nested or derived classes.

Interfaces

Defines a set of public methods an adhering class must implement

interface IExample {
  Method() : void;
}

class Example : IExample {
  public Method () : void { ... }
}

Properties

Properties are syntactic sugar for using setter/getter methods.

class Example{
  private mutable minutes: int;

  public Hours : int {
    get { minutes/60 }
    set { minutes = 60*value }  //a setter can be excluded to create a read-only variable
  }

  public Minutes: int {
    get { minutes }
    set { minutes = value }
  }
}

Property Accessors

  • [Accessor (Sum)] mutable sum : int;: generates a public property named Sum, getting the value from the field sum. Only a getter is generated.
  • [Accessor (Sum, flags=Internal)] mutable sum : int;: changes the accessibility
  • [Accessor (Sum, flags=WantSetter)] mutable sum : int;: generates both a getter and setter
  • [Accessor] mutable sum_of_sam : int;: generates the property SumOfSam
more...

Operator Overloading

Operator overloads are defined using @:

[Record] //Creates a constructor which sets all fields in the order defined.
class Vector {
  [Accessor] x : double; //Creates a property X
  [Accessor] y : double;
  [Accessor] z : double;

  // + operator
  public static @+ (v1 : Vector, v2 : Vector) : Vector {
    Vector (v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z)
  }

  // Overrides the implicit cast operator to allow the conversion of a Point to a Vector.
  public static @: (p : Point) : Vector {
    Vector (p.X, p.Y, p.Z)
  }

  // Overrides the explicit cast operator to allow the conversion of a double to a Vector.
  public static @:> (p : double) : Vector {
    Vector (p, 0.0, 0.0)
  }
}

Implicit casts (the : operator) will be automatically used by the compiler to convert from one type to another when assigning values or passing arguments to functions. Explicit casts (the :> operator) must be manually stated.

Hence:

needsVector(somePoint); //A Point can be passed to a function which needs a Vector without writing out a cast.
needsVector(someDouble :> Vector); //A double must be explicitly converted

Indexers

Indexers allow access to an internal storage array within a class:

class Table [T]{
  private storage : array[array[T]];
  
  this(width : int, height : int){
    //initialize storage
  }

  public Item[row : int, column : int] : T {
    get { storage[row][column]}
    set { storage[row][column] = value;}
  }
}

def someTable = Table(11,11);
t[3,1] = 4159265;

Design By Contract

With Methods

Methods may use design by contract to check arguments and values before or after running. The namespace Nemerle.Assertions must be imported to do this.

using Nemerle.Assertions;

public class Example{
  public DoSomething(mutable i : int) : int
    requires 0 <= i && i <= 5 otherwise throw System.ArgumentException() //Checked before running.
    ensures CleanUp() otherwise throw System.Exception("Clean Up failed.") //Checked after running.
    {...}
}

If otherwise is not included and a contract is violated, then a Nemerle.AssertionException will be thrown by default.

Parameters may also be checked individually:

ConnectTrees (requires (!tree1.Cyclic ()) tree1 : Graph,
              requires (!tree2.Cyclic ()) tree2 : Graph, 
              e : Edge) : Graph
{ ... }

Using Invariants

An invariant is a statement/function which should always evaluate to true. Classes declare invariants as follows:

using Nemerle.Assertions;

class Example
invariant IntegrityCheck() //A public function or one which uses expose can never be used here.
{
  mutable x : int;
  mutable y : int;

  private IntegrityCheck() : bool{
    if(x>0)
      x <= y && y <= 99
    else
      x != y
  }
}

Invariants will be evaluated when public functions return and whenever expose is used:

expose (this) {
  x += 3;
  y += 3;
}//Invariants will be checked at the end of this block of code.

An assertion exception is raised if an invariant returns false.

more...

Namespaces

Namespaces are objects which may hold classes, modules, and other namespaces.

  • namespace SomeSpace { ... }: declares a new namespace
  • using SomeSpace;: opens the namespace (All objects in SomeSpace are now in scope)
  • using System.Console;: opens the type, making its static methods visible
  • using C = System.Console;: creates an alias referring to a type/namespace

Extension Methods

Extension methods are syntactic sugar for using a method from a module in a namespace on an object: MathSpace.Functions.Root(someInt) could be shortened to someInt.Root() for example.

To create extension methods, create a module inside of a namespace, and then add public methods whose first parameter is named 'this':

namespace ExtensionSpace {
  module Extensions {
    public PlusOne (this i : int) {
      i + 1
    }
    public PlusOne (this i : string) {
      i + "1"
    }
  }
}

To use extension methods, simply import the namespace where they were defined:

using ExtensionSpace;

def a = 3;
def b = "string";
WriteLine(a.PlusOne());
WriteLine(b.PlusOne());

A class's public static members could also be used to create extension methods, but creating a separate module for them is clearer.

more...

Generics

An example of a generic class and methods:

public class Example[T] {
  public mutable item : T;
  public foo [Z] (val : Z) : Z {
    val;
  }
  public bar [C] () : void {}
}

Generic types can be inferred:

def a = Example(); //The class from above.
a.item = "Using Nemerle prevents carpal tunnel syndrome!";
System.Console.WriteLine(a.item);
  • Generic functions, variants, and methods all use the same syntax.
  • def x = Example[int].bar.[string]();: explicitly sets types. For the rare cases in which generic parameters cannot be inferred

Generic Type Constraints

  • class Example[T] where T : IComparable[T] { ... }: T must implement or inherit the class or interface stated
  • class Example[T] where T : class {...}: T must be a reference type
  • class Example[T] where T : struct {...}: T must be a value type
  • class Example[T] where T : enum {...}: T must be an enumeration type
  • class Example[T] where T : new () {...}: T must implement a parameterless constructor. This is the only way to create a new T within Example.

Covariance and Contravariance

  • class Example[+T] { ... }: makes the argument type covariant. Covariant type parameters can only be used as return types.
  • class Example[-T] { ... }: makes the argument type contravariant. Contravariant type parameters can only be used as argument types or in generic interfaces.
Covariance Defined: If Child extends Parent, an instance of Example[Child] could be assigned to a variable of type Example[Parent]. An instance of Example[string] could be assigned to an Example[object] variable.

Contravariance Defined: If Child extends Parent, an instance of Example[Parent] could be assigned to a variable of type Example[Child]. An instance of Example[object] could be assigned to an Example[string] variable.

Reflection

The 'is' Keyword

Runtime type-checking can be performed using is:

def someObject = getSomeObject();
when(someObject is string){
  (someObject  :> string).ToUpper();
}
The above can be done more efficiently with pattern matching.

Standard Types

Lists

  • def nil = [];: an empty list
  • def numbers = [1, 2, 3];: creates a list with the values between the brackets
  • def more_numbers = 4 :: numbers; : :: adds a new item to the list
  • def list3 = list1 + list2; : :: concatenates two lists

List Comprehensions

  • $[ (x, y) | x in list1, y in list2, x < y]: gets all pairs on the lists for which the condition is true.
  • $[1 .. 5]: integers 1 through 5 stepping by 1
  • $[1, 3 .. 8]: integers 1 through 8 stepping by 2 (3-1 = 2)
  • $[ (x, y) | x in [1 .. 3], y in [2, 4 .. 10] ]: generates all pairs
more...

Useful Properties on Lists

  • Length : int: the length of the list
  • Head : T: the first element in the list
  • Last : T: the last element in the list
  • Tail : list[T]: a new list starting at the old list's second element

Useful Methods on Lists

  • FirstN(int : n) : list[t]: returns the first N elements of the list
  • ChopFirstN(int : n) : list[t]: returns the list without the first N elements
  • LastN(int : n) : list[t]: returns the last N elements of the list
  • Reverse() : list[T]: returns the reversed list
  • Remove(item : T) : list[T]: returns the list with all instances of item removed
  • Contains(item : T) : bool returns true/false if the list does/doesn't contain item
  • Iter (f : T -> void) : void: calls the given function using each element in the list as an argument
  • Map (f : T -> T') : list[T']: executes f using each element in the list[T] and returns the results as a new list[T']
  • Sort (cmp : T * T -> int): returns a new sorted list. Equality is determined by f, a function which compares two Ts and returns 1, 0, or -1 if the first T is greater than, equal to, or less than the second.
  • Group (f : T * T -> int) : list[list[T]]: Returns a list[list[T]] where each sub-list contains all elements which are considered equal. Equality is determined as in Sort()
  • FoldLeft(acc : T', f : T*T' -> T') : T': returns the result of executing f recursively on the list. more details
  • FoldRight(acc : T', f : T*T' -> T') : T': like FoldLeft(), but works through the list backwards.
  • ForAll (f : T -> bool) : bool: returns true if f returns true for all items in the list
  • Exists (f : T -> bool) : bool: returns true if f returns true for any item in the list
  • Find (pred : T -> bool) : Option[T]: returns, as an option, the first element pred returns true with
  • Filter (pred : T -> bool) : list[T]: returns a new list containing all elements pred returns true with
  • RemoveDuplicates() : void: removes duplicate elements
  • ToArray() : array[T]: returns the list as an array
Most of the BCL collection types have extended counterparts in the Nemerle library which contain many of the above methods. Nemerle.Collections.Hashtable extends System.Collections.Generic.Dictionary for example.

Arrays

  • def a = array(3);: specifies the number of elements
  • def b = array(2, 3): specifies the number of elements in a multi-dimensional array
  • def c = array[1, 2, 3]: initializes with the specified elements
  • def d = array.[2] [ [1, 2, 3], [4, 5, 6] ]: initializes a multidimensional array with the specified elements
You can also specify array type:
def a = array(3) : array[byte];
def b = array[1 : byte, 2, 3];
mutable c : array[byte];

Tuples

  • def t = (1, "one", 'c');: a tuple is a set of values surrounded by parentheses
  • int*string*char: is the type of the above tuple
  • def fst = t[0];: tuples use a 0-based index to retrieve items

Nemerle.Utility.Pair

This class contains three methods that work with pairs (tuples of two elements):

  • First and Second retrieve the first or second element of the tuple, respectively
  • Swap exchanges both elements of the pair

Variants

  • The following creates a variant with two empty options and one option with extra information:
variant Volume {
  | Max
  | Min
  | Other { v : int }
}
  • def v1 = Volume.Max ();: an empty constructor is used for empty options
  • def v2 = Volume.Other (5);: the constructor for non-empty elements uses all fields as arguments (in the order they were written)
  • You need to include [XmlSerializable] on top of a variant to make it serializable via XML

Enums

As in C#, enums are just syntactic sugar for a list of ints:

enum Seasons {
  | Spring
  | Autumn
  | Winter
  | Summer
}

FlagAccessor

The FlagAccessor macro automatically creates boolean properties from an internal enumeration:

class Person{
  [System.Flags] enum States {
    | Working = 0x0001
    | Married = 0x0002
    | Graduate = 0x0004
  }

  [FlagAccessor (Working, Married, Graduate, flags=WantSetter)] //Will be read-only without flags=WantSetter
  mutable state : States; //Value stored here.
}

def casey = Person()
casey.Working = true;
casey.Married = false;

Nullable Primitives and Options

Nullable Primitives

Primitives cannot be null (as a platform requirement). However, sometimes nullable integers or booleans are needed.

  • def t : int? = 3;: declares a new nullable int whose value is 3
  • def t : int? = null;: declares a new nullable int whose value is null
  • when (t != null) {...}: is the same as when (t.HasValue) {...}
  • a = b ?? -1;: a is set to the wrapped value of b if b isn't null. Otherwise, a is set to the value after the two question marks
  • a = b?.P;: Returns P if b is not null. Otherwise returns the default value of P)'s type. Avoids a NullReferenceException. b must be a reference type.

Options

Options are variants defined as follows:

public variant option[T]{ //A simplified version.
    | None  //has no value saved
    | Some { val : T; } //Has some saved value 'val'
}
  • option.Value is a property used to access Some.val. It will throw an exception if the option's type is None.
Options are useful when a function needs to return a value which signifies "No Result", but returning null doesn't make sense. For example:
def li = ["list","of","strings","with","no","null","values"];
def li2 = ["list","with",null];
li.Find(_ == null) //returns None // _ == null is the same as fun(x){x == null}
li2.Find(_ == null).Value //returns null

Control Flow

If-Statements And The Like

Nemerle if-statements are slightly different from (clearer than) most languages. All if statements must be followed by an else statement. To replace solitary if statements, the 'when' and 'unless' constructs have been added.

  • when (condition) code: if condition is true, then execute code
  • unless (condition) code: if condition is false, then execute code
  • if (condition) code1 else code2: if condition is true, then execute code1. Otherwise, execute code2

Loops

  • while (condition) code: execute code while condition is true
  • do code while (condition): the same as above, but checks the condition at the end of the code, so it is always run at least once
  • for (mutable i = 0; i < 10; i++) code: A standard C-style for-loop.
  • foreach (i in $[0..9]) code like the above, using a range
  • foreach (i in $[9,8..0]) code reverse order
  • foreach (x in list) code: execute the code for every member x of the enumerable collection list
  • foreach (x in list with index) code: like the above with available collection index
  • foreach (x when x != null in list) code: like the above but only for members which aren't null
  • repeat (10) code: repeats the code the specified number of times

Pattern Matching

Pattern matching checks if a variable or object matches a given pattern. If a match is found, then a specific bit of code is executed.

match(object)
  | pattern1 => doSomething
  | pattern2 when(someCondition) => doSomethingElse //when statements may optionally be used with a pattern
  | pattern3
  | pattern4 => doAnotherThing //Matching either pattern3 or pattern4 will execute this line.
  | _ => doSomeOtherThing //Underscore represents a "wildcard". All variables will always match it.

Patterns are matched against from top to bottom and only one pattern can be matched.

Matching By Object Value (Simple Equality)

Pattern matching can most simply be used to check for equality. This functions much like a nicer looking switch statement:

  • Numbers:
match (value) {
  | 1 => WriteLine("one");
  | 2 => WriteLine("two");
  | _ => WriteLine("more");
}
  • Strings:
match (value) {
  | "one" => WriteLine(1);
  | "two" => WriteLine(2);
  | _ => WriteLine(0);
}
  • Enums:
match (value) {
  | Seasons.Spring
  | Seasons.Summer => WriteLine("hot");
  | _ => WriteLine("cold");
}

Matching By Type

An object can also be matched against Types:

def check (o : object) {
  match (o) {
    | int => WriteLine("o is an int")
    | string => WriteLine("o is a string");
    | _ => WriteLine("o isn't an int or a string");
  }
}

Matching Using Object Members

More advanced pattern matching can compare object members:

def killWhenFlanders (someone) {
  match (someone) {
    | Person where(name = "Flanders") => kill(someone)
    | _ => () //Do nothing.
  }
}

If the type is known to the compiler (most of the time), the class name and where clause aren't needed

def killWhenFlanders (someone) {
  match (someone) {
    | (name = "Flanders") => kill(someone)
    | _ => () //Do nothing.
  }
}
With List Members

Lists can be matched against patterns resembling:

match (someList) {
  | [42, 42] => WriteLine("This list has two elements which are both 42.")
  | [42, _] => WriteLine("This list has two elements and the first is 42.")
  | 42 :: _ => WriteLine("The first element in this list is 42.")
  | _ :: 42 :: _ => WriteLine("The second element in this list is 42.")
  | [] => WriteLine("The list is empty!")
  | _ => WriteLine("The list doesn't fit any of these patterns")
}
With Tuple Members

Tuples can be matched against patterns like the following:

match (tuple) {
  | ( 42, _ ) => "This tuple has 42 in the first position."
  | ( _, 42 ) => "This tuple has 42 in the second position."
}
'''

===== With Variants =====
```nemerle
variant Water{
  | Liquid { depth : int; }
  | Gas { density : float }
  | Solid { pieces : int; volume : float }
}
 
match (someWater) {
  | Liquid (1) => WriteLine("The water has a depth of 1.")
  | Solid (4, _) => WriteLine("There are four pieces of ice.")
  | Gas => WriteLine("Steam!")
}

Binding Variables During Pattern Matching

Objects may be bound to variables during pattern matching. This is especially useful when matching functions.

Binding With 'as'

The as keyword binds the object being matched to a new variable if it matches the adjacent pattern:

match (getPerson()) {
  | (name = "Flanders") as ned => ned.die()
  | _ as somePerson => WriteLine($"Hello, $(somePerson.name)!")
}
Binding With 'is'

The is keyword binds the object being matched to a new variable based on its type:

def check (o : object) {
  match (o) {
    | i is int => $"An int: $i"
    | s is string => $"A string: $(s.ToUpper())"
    | _ => "Object of another type"
  }
}
Binding Members

Members of a some objects may also be bound to variables:

match (list) {
| head :: tail => Write ($ "Head: $head, Tail: $tail")
| [] => WriteLine ("empty")
}
Binding Members Using 'with'

with is used to specify a default value for a member in cases where we need to match similarly structured objects using the same code.

def getArea(_) {
  | [x] with y = 0
  | [x, y] => x * y
  | _ => 0
}
Binding Members Using Explicit Types

If for some reason the compiler cannot infer the type of a member, it can be explicitly stated:

def foo (l) {
  | (s : string) :: _ => s [0]
  | [] => '?'
}

Matching Regular Expressions

http://en.wikipedia.org/wiki/Regular_expression

using Nemerle.Text// Nemerle.Text is required to use this functionality

regexp match (str) {
  | "a+.*" => printf ("a\n");
  | @"(?<num : int>\d+)-\w+" => printf ("%d\n", num + 3);
  | "(?<name>(Ala|Kasia))? ma kota" =>
     match (name) {
       | Some (n) => printf ("%s\n", n)
       | None => printf ("noname?\n")
     }
  | _ => printf ("default\n");
}

Exceptions

  • throw ArgumentException();: throws a new exception of the desired type

Try/Catch/Finally

  • Nemerle's try-catch-finally syntax resembles that of C#. The major difference is that catch handlers use Nemerle's pattern matching syntax:
try {
  code
}
catch {
  | e is ArgumentNullException => ...
  | e is OverflowException => ...
  | e is MyException when e.Reason == Reason.Warn => ...
  | _ => ...
}
finally {
  // Finally code is always executed;
  // whether an exception was thrown or not.
}

Assertions

  • assert (condition, "message");: if condition is false, an AssertionException will be thrown

Imperative Programming and Blocks

To use return, break and continue the Nemerle.Imperative namespace must be used:

  • return x;: stops the execution of a function/method/property and returns x
  • break;: stops the execution of a loop and moves on to the next bit of code
  • continue;: skips to the next iteration of a loop
Alternatively, "block" constructs may be used. A block is a set of instructions preceded with an identifier and a colon. The result of a block is either the last value computed or some value returned using the name of the block:
def x =
  foo: {
    when (some_cond) foo(3); // if some_cond is true, the block will return 3
    qux ();
    42 // else it will return 42
  }

more...

The Nemerle Standard Library

Collection Classes

The Nemerle Collection classes/interfaces are all contained in the Nemerle.Collections namespace.

Many of these classes extend items from the .NET Collections library with useful methods like Iter, Fold, and Map:

  • ICollection[T]: extends System.Collections.ICollection[T]
  • Hashtable[K, V]: extends System.Collections.Generic.Dictionary[K, V]
  • Stack[T]: extends System.Collections.Stack
  • LinkedList[T]: extends System.Collections.GenericLinkedList[T]
  • Queue[T]: extends System.Collections.Queue[T]
Others provide additional functionality:
  • Tree an implentation of the Red-Black tree data structure
  • RList a Random Access List (a purely functional data structure)
  • Heap: a heap data structure. Only the top of the heap can be accessed.
  • Set[T]: an implementation of mathematical sets. It has all of list's operations as well as:
    • Sum: adds two sets yielding only one replica of each duplicated element
    • Subtract: returns all elements of the first set that are not in the second one
    • Intersect: return the elements that appear on both sets
    • Xor: return the elements that appear in one of the sets, but not in both

Array and String Extensions

Using the Nemerle.Utility namespace will allow the use of extension methods in the Nemerle.Utility.NArray and Nemerle.Utility.NString classes. The methods add list-like functionality to arrays and strings.

Nemerle.IO

The following functions are contained in the Nemerle.IO namespace.

Print Functions

  • print(str), sprint(str), fprint(str): use $-notation to substitute variable names with their values before printing to the Console, a string, or a TextReader (respectively)
  • printf (value), sprintf, fprintf: as above, but with C-Style formatting instead of $-notation
  • scanf(format), sscanf(format), fscanf(format): returns a string extracted from the console, a string, or a TextReader with the specified format

Input Stream Functions

The Nemerle.IO namespace contains useful functions for handling an input stream: ReadIntDigits, ReadRealDigits, ReadString, ReadChar, ConsumeWhiteSpace, and CheckInput.

Other Constructs/Macros

Lazy Evaluation

  • def l = lazy( MethodWithBigCost() );: declares a variable whose value will only be assigned if it is used
  • someFunction([Lazy] x : int, y : int) { ... }: if passed a lazy argument, the function will only evaluate it if it is used
The field Next will only be evaluated when requested, because of its LazyValue type:
class InfiniteList {
  public Val : int;
  public Next : LazyValue [InfiniteList];
 
  public this (v : int) {
    Val = v;
    Next = lazy(InfiniteList(v + 1)); 
  }
}

more...

Late Binding

Late expressions are executed dynamically at runtime (which makes them perfect for things like COM). No strict type-checks are done, so be wary.

  • late(expr) or late expr: executes a valid Nemerle expression with late binding.
  • nolate (expr) or nolate expr: allows executing a expression as if it wasn't late in a late binding environment.
  • late obj.Length: the Length property or field is retrieved at runtime and can be used with strings, lists, arrays, etc without worrying about their type. If the object does not contain a Length member, an exception will be thrown during runtime.
  • late o.Name.[1]: calls the default indexer on o.Name. You should be aware that calling o.Name.Chars[1] calls the indexer named Chars on o.Name.
more...

Aliases

Type Alias

  • type int = System.Int32;: establishes an alias for the System.Int32 type. int can be used anywhere in code with the exact same meaning as System.Int32.

Alias Macro

The alias macro will transform

[Alias (F2, F3 ())]
public static F () : int { System.Random ().Next () }
into
public static F () : int { System.Random ().Next () }
public static F2 : int { get { System.Random.Next () } }
public static F3 () : int { System.Random.Next () }

Method arguments used in the macro must match those in the method being aliased, though their order can be changed. Arguments not specified will pass this:

[Alias ( Add(b) )]
public static Add (a : MyClass, b : MyClass) : MyClass { 
  //return a new MyClass using the values 'a' and 'b'.
}

[Alias ( Root )]
public static Root (a : MyClass) : MyClass { 
  //return a new MyClass with the square root value of 'a'
}
Goes to:
public static Add (a : MyClass, b : MyClass) : MyClass { 
  //return a new MyClass using the values 'a' and 'b'.
}
public Add(b : MyClass) : MyClass{
  def a = this;
  //return a new MyClass using the values 'a' and 'b'.
}

public static Root (a : MyClass) : MyClass { 
  //return a new MyClass with the square root value of 'a'
}
public Root : MyClass {
  get{
    def a = this; //return a new MyClass with the square root value of 'a'
  }
}

Yield

  • Works as in C#, used for enumerating sequences:
Range (from : int, to : int) : IEnumerable[int] {
  for (mutable i = from; i <= to; i++)
    yield i;
}

Delegates and Events

  • delegate Foo(_ : int, _ : string) : void;: creates a new delegate type
  • def f1 = Foo(f);: creates a delegate containing the method/function f
  • def f2 = Foo( fun(i, s) { ... } );: creates a delegate containing an anonymous function
  • event MyEvent : Foo;: creates an event field of delegate type Foo
  • Foo += method;: adds a method to the delegate
  • Foo -= method;: removes a method from the delegate
  • Foo(2, "two");: invokes a delegate (calls all the methods in it, in the order they were added) with the given arguments

Concurrency

Lock

  • lock (object) { ... }: Synchronizes access to the block of code for object. object is usually this.

Concurrency Macros

The following macros are contained in the Nemerle.Concurrency namespace:

  • async { ... }: executes a block of code asynchronously
  • async Read (s : string) { ... } creates a method that will always execute asynchronously
Chords

A "chord" is a method which will block (halt the execution of its thread) unless/until one of its "ChordMembers" has been called. Once a ChordMember has been called, the chord will execute some code associated with the member and return a value:

//Any thread can access Add() and Mul(), but only threads which call CalculateNext() will do any processing.
class ThreadedCalculator
{
    [ChordMember]
    public Add (a : int, b : int) : void;
    [ChordMember]
    public Mul (a : int, b : int) : void;

    public CalculateNext () : int
    chord {
      | Add => a + b
      | Mul => a * b
    }
}

Calls to ChordMembers are queued for the chord in a "Last In First Out" order.

Interactive Chord Example

More examples can be found at the SVN repository

The using() Construct

  • using (resource) { ... }: This block assures that resource is correctly disposed of even if an exception is thrown. resource must implement the IDisposable interface.

Unchecked Numeric Overflow

  • unchecked { ... }: within the unchecked scope numerical operations will silently overflow
  • checked { ... }: operations will throw an OverflowException like they normally would. Can be used inside of an unchecked context.

Logging Macros

All logging macros/functions are inside the Nemerle.Logging namespace:

  • [assembly: LogFunction (DoLog)]: specifies a logging function
  • [assembly: LogFunction (DEBUG => log4net_category.Debug, TRACE => log4net_category.Info)]: specifies different logging functions for different compilation flags
  • log (VERB, "example", 2): calls the logging function, if the compilation flag is set, with the given parameters
  • whenlogging (VERB) { code }: executes the code only if the compilation flag is set
  • [assembly: LogFlag (VERB, true)]: sets the compilation flag for VERB to true
  • [assembly: LogCondition (EnableLogging), LogFlag (DEBUG, true)]: add a condition that will be checked each time the log function is called
  • [assembly: LogFormat (PrependFlag)]: prepends a flag to each message
more...

Profiling Macros

  • [assembly: ProfSetup]: initializes the profiler
  • [Profile] foo () : int { ...}: tells the profiler to include foo
  • [ProfDump] Dump () : void { }: each call to this method will show the profiler's results
more...

SQL Macros

  • [ConfigureConnection (connectionString, name)]: is applied to a class. It tells the compiler about the connections used later on in other SQL macros
  • ExecuteNonQuery ("INSERT INTO employee VALUES ('John', 'Boo')", connection);: executes a query returning no results, via the specified connection
  • def count = ExecuteScalar ("SELECT COUNT FROM employee WHERE firstname = $myparm", connection);: retrieves a query with one result, using the specified connection. Notice how $-notation is used to substitute variables
  • The following executes the given code using every returned result:
ExecuteReaderLoop (
    "SELECT * FROM employee WHERE firstname = $myparm",
    dbcon, 
    {
      WriteLine($"Name: $firstname $lastname")
    }
);

more...

Clone this wiki locally