-
Notifications
You must be signed in to change notification settings - Fork 89
Quick guide
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:
|
-
mutable a = 5;
: mutable variables can be changed later -
def b = 4;
: normal variables cannot be changed
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
-
a = b;
: assignment. This operator doesn't return a value, soa = b = c
is not allowed- Compound assignment is allowed:
a += b;
- Compound assignment is allowed:
-
+
: 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
-
==, !=
: 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 theEquals
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
and/or/not
by opening the Nemerle.English
namespace
-
&, |, ^
: bit-level and/or/xor operations -
%&&, %||, %^^
: bit-level and/or/xor, returning true if the result is non-zero -
>>, <<
: right/left bit-shifting
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 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 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.
}
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)!");
}
}
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);
}
}
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.
}
int*char*string -> long
Note that parameter types use the same notation as tuple types.
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
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.
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
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;
}
}
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
-
class Example { ... }
: creates a class Example -
module Example { ... }
: creates a module Example. A module is a class in which all members are static.
-
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.
For Methods and Fields
-
static
: Only a single instance exists. This instance is accessed statically:ClassName.StaticMember()
-
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.
-
extern
: Used withDllImportAttribute
to call native code.
-
partial
: Only used with type definitions. Indicates a given type definition is split across several files.
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.
Executed once per type. No parameters.
class Example {
static public this() { ... }
}
Generates a constructor which assigns a value to every field:
[Record] class Point {
public x : int; public y : int;
}
class Point {
public x : int; public y : int;
public this (x : int, y : int) {
this.x = x; this.y = y
}
}
-
class Human : Mammal { ... }
: class Human inherits from Mammal or implements the interface Mammal
-
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.
Defines a set of public methods an adhering class must implement
interface IExample {
Method() : void;
}
class Example : IExample {
public Method () : void { ... }
}
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 }
}
}
-
[Accessor (Sum)] mutable sum : int;
: generates a public property namedSum
, getting the value from the fieldsum
. 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 propertySumOfSam
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 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;
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
{ ... }
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.
Namespaces are objects which may hold classes, modules, and other namespaces.
-
namespace SomeSpace { ... }
: declares a new namespace -
using SomeSpace;
: opens the namespace (All objects inSomeSpace
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 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.
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
-
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.
-
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.
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.
Runtime type-checking can be performed using is
:
def someObject = getSomeObject();
when(someObject is string){
(someObject :> string).ToUpper();
}
-
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
-
$[ (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
-
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
-
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 ofitem
removed -
Contains(item : T) : bool
returns true/false if the list does/doesn't containitem
-
Iter (f : T -> void) : void
: calls the given function using each element in the list as an argument -
Map (f : T -> T') : list[T']
: executesf
using each element in thelist[T]
and returns the results as a newlist[T']
-
Sort (cmp : T * T -> int)
: returns a new sorted list. Equality is determined byf
, a function which compares twoT
s 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 alist[list[T]]
where each sub-list contains all elements which are considered equal. Equality is determined as inSort()
-
FoldLeft(acc : T', f : T*T' -> T') : T'
: returns the result of executingf
recursively on the list. more details -
FoldRight(acc : T', f : T*T' -> T') : T'
: likeFoldLeft()
, but works through the list backwards. -
ForAll (f : T -> bool) : bool
: returns true iff
returns true for all items in the list -
Exists (f : T -> bool) : bool
: returns true iff
returns true for any item in the list -
Find (pred : T -> bool) : Option[T]
: returns, as an option, the first elementpred
returns true with -
Filter (pred : T -> bool) : list[T]
: returns a new list containing all elementspred
returns true with -
RemoveDuplicates() : void
: removes duplicate elements -
ToArray() : array[T]
: returns the list as an array
Nemerle.Collections.Hashtable
extends System.Collections.Generic.Dictionary
for example.
-
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
def a = array(3) : array[byte];
def b = array[1 : byte, 2, 3];
mutable c : array[byte];
-
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
This class contains three methods that work with pairs (tuples of two elements):
-
First
andSecond
retrieve the first or second element of the tuple, respectively -
Swap
exchanges both elements of the pair
- 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
As in C#, enums are just syntactic sugar for a list of ints:
enum Seasons {
| Spring
| Autumn
| Winter
| Summer
}
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;
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 aswhen (t.HasValue) {...}
-
a = b ?? -1;
:a
is set to the wrapped value ofb
ifb
isn't null. Otherwise,a
is set to the value after the two question marks -
a = b?.P;
: ReturnsP
ifb
is not null. Otherwise returns the default value ofP
)'s type. Avoids a NullReferenceException.b
must be a reference type.
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 accessSome.val
. It will throw an exception if the option's type is None.
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
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
-
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 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.
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");
}
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");
}
}
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.
}
}
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")
}
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!")
}
Objects may be bound to variables during pattern matching. This is especially useful when matching functions.
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)!")
}
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"
}
}
Members of a some objects may also be bound to variables:
match (list) {
| head :: tail => Write ($ "Head: $head, Tail: $tail")
| [] => WriteLine ("empty")
}
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
}
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]
| [] => '?'
}
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");
}
-
throw ArgumentException();
: throws a new exception of the desired type
- 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.
}
-
assert (condition, "message");
: ifcondition
is false, an AssertionException will be thrown
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
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
}
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]
: extendsSystem.Collections.ICollection[T]
-
Hashtable[K, V]
: extendsSystem.Collections.Generic.Dictionary[K, V]
-
Stack[T]
: extendsSystem.Collections.Stack
-
LinkedList[T]
: extendsSystem.Collections.GenericLinkedList[T]
-
Queue[T]
: extendsSystem.Collections.Queue[T]
-
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 oflist
'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
-
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.
The following functions are contained in the Nemerle.IO namespace.
-
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
The Nemerle.IO namespace contains useful functions for handling an input stream: ReadIntDigits, ReadRealDigits, ReadString, ReadChar, ConsumeWhiteSpace, and CheckInput
.
-
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
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));
}
}
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)
orlate expr
: executes a valid Nemerle expression with late binding. -
nolate (expr)
ornolate expr
: allows executing a expression as if it wasn't late in a late binding environment. -
late obj.Length
: theLength
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 aLength
member, an exception will be thrown during runtime. -
late o.Name.[1]
: calls the default indexer on o.Name. You should be aware that callingo.Name.Chars[1]
calls the indexer namedChars
ono.Name
.
-
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.
The alias macro will transform
[Alias (F2, F3 ())]
public static F () : int { System.Random ().Next () }
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'
}
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'
}
}
- Works as in C#, used for enumerating sequences:
Range (from : int, to : int) : IEnumerable[int] {
for (mutable i = from; i <= to; i++)
yield i;
}
-
delegate Foo(_ : int, _ : string) : void;
: creates a new delegate type -
def f1 = Foo(f);
: creates a delegate containing the method/functionf
-
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
-
lock (object) { ... }
: Synchronizes access to the block of code forobject
.object
is usuallythis
.
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
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.
More examples can be found at the SVN repository
-
using (resource) { ... }
: This block assures thatresource
is correctly disposed of even if an exception is thrown.resource
must implement theIDisposable
interface.
-
unchecked { ... }
: within the unchecked scope numerical operations will silently overflow -
checked { ... }
: operations will throw anOverflowException
like they normally would. Can be used inside of an unchecked context.
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
-
[assembly: ProfSetup]
: initializes the profiler -
[Profile] foo () : int { ...}
: tells the profiler to includefoo
-
[ProfDump] Dump () : void { }
: each call to this method will show the profiler's results
-
[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")
}
);