标签:
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 laterdef b = 4;
: normal variables cannot be changedThe 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 Nemerlea = b;
: assignment. This operator doesn‘t return a value, so a = b = c
is not allowed
a += b;
+
: addition and string/list concatenation-, *, /, %
: subtraction/multiplication/division/moduloa <-> b;
: swap (exchanges the values of a and b)++, --
: adds/subtracts 1 to/from a value==, !=
: equal/not equalx.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!
: notYou can also use 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-shiftingThe 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 idef 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 variablesFunctions 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.
}
is:
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.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
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 Examplemodule 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()
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 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;
}
is the same as:
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 Mammalabstract
: 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 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
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 namespaceusing SomeSpace;
: opens the namespace (All objects in SomeSpace
are now in scope)using System.Console;
: opens the type, making its static methods visibleusing C = System.Console;
: creates an alias referring to a type/namespaceExtension 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);
def x = Example[int].bar.[string]();
: explicitly sets types. For the rare cases in which generic parameters cannot be inferredclass Example[T] where T : IComparable[T] { ... }
: T must implement or inherit the class or interface statedclass Example[T] where T : class {...}
: T must be a reference typeclass Example[T] where T : struct {...}
: T must be a value typeclass Example[T] where T : enum {...}
: T must be an enumeration typeclass 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.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.
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.
def nil = [];
: an empty listdef numbers = [1, 2, 3];
: creates a list with the values between the bracketsdef more_numbers = 4 :: numbers;
: :: adds a new item to the listdef 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 pairsLength : int
: the length of the listHead : T
: the first element in the listLast : T
: the last element in the listTail : list[T]
: a new list starting at the old list‘s second elementFirstN(int : n) : list[t]
: returns the first N elements of the listChopFirstN(int : n) : list[t]
: returns the list without the first N elementsLastN(int : n) : list[t]
: returns the last N elements of the listReverse() : list[T]
: returns the reversed listRemove(item : T) : list[T]
: returns the list with all instances of item
removedContains(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 argumentMap (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 T
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 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 detailsFoldRight(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 listExists (f : T -> bool) : bool
: returns true if f
returns true for any item in the listFind (pred : T -> bool) : Option[T]
: returns, as an option, the first element pred
returns true withFilter (pred : T -> bool) : list[T]
: returns a new list containing all elementspred
returns true withRemoveDuplicates() : void
: removes duplicate elementsToArray() : array[T]
: returns the list as an arrayMost of the BCL collection types have extended counterparts in the Nemerle library which contain many of the above methods. Nemerle.Collections.Hashtable
extendsSystem.Collections.Generic.Dictionary
for example.
def a = array(3);
: specifies the number of elementsdef b = array(2, 3)
: specifies the number of elements in a multi-dimensional arraydef c = array[1, 2, 3]
: initializes with the specified elementsdef d = array.[2] [ [1, 2, 3], [4, 5, 6] ]
: initializes a multidimensional array with the specified elementsYou can also specify array type:
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 parenthesesint*string*char
: is the type of the above tupledef fst = t[0];
: tuples use a 0-based index to retrieve itemsThis class contains three methods that work with pairs (tuples of two elements):
First
and Second
retrieve the first or second element of the tuple, respectivelySwap
exchanges both elements of the pairvariant Volume {
| Max
| Min
| Other { v : int }
}
def v1 = Volume.Max ();
: an empty constructor is used for empty optionsdef v2 = Volume.Other (5);
: the constructor for non-empty elements uses all fields as arguments (in the order they were written)[XmlSerializable]
on top of a variant to make it serializable via XMLAs 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 3def t : int? = null;
: declares a new nullable int whose value is nullwhen (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 marksa = 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 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
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 codeunless (condition) code
: if condition is false, then execute codeif (condition) code1 else code2
: if condition is true, then execute code1. Otherwise, execute code2while (condition) code
: execute code while condition is truedo code while (condition)
: the same as above, but checks the condition at the end of the code, so it is always run at least oncefor (mutable i = 0; i < 10; i++) code
: A standard C-style for-loop.foreach (i in $[0..9]) code
like the above, using a rangeforeach (i in $[9,8..0]) code
reverse orderforeach (x in list) code
: execute the code for every member x of the enumerable collection listforeach (x in list with index) code
: like the above with available collection indexforeach (x when x != null in list) code
: like the above but only for members which aren‘t nullrepeat (10) code
: repeats the code the specified number of timesPattern 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:
match (value) {
| 1 => WriteLine("one");
| 2 => WriteLine("two");
| _ => WriteLine("more");
}
match (value) {
| "one" => WriteLine(1);
| "two" => WriteLine(2);
| _ => WriteLine(0);
}
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 typetry {
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");
: if condition
is false, an AssertionException will be thrownTo use return
, break
and continue
the Nemerle.Imperative
namespace must be used:
return x;
: stops the execution of a function/method/property and returns xbreak;
: stops the execution of a loop and moves on to the next bit of codecontinue;
: skips to the next iteration of a loopAlternatively, "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
}
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 structureRList
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 elementSubtract
: returns all elements of the first set that are not in the second oneIntersect
: return the elements that appear on both setsXor
: return the elements that appear in one of the sets, but not in bothUsing the Nemerle.Utility
namespace will allow the use of extension methods in theNemerle.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 $-notationscanf(format), sscanf(format), fscanf(format)
: returns a string extracted from the console, a string, or a TextReader with the specified formatThe 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 usedsomeFunction([Lazy] x : int, y : int) { ... }
: if passed a lazy argument, the function will only evaluate it if it is usedThe 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));
}
}
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
.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 () }
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‘
}
}
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 typedef f1 = Foo(f);
: creates a delegate containing the method/function f
def f2 = Foo( fun(i, s) { ... } );
: creates a delegate containing an anonymous functionevent MyEvent : Foo;
: creates an event field of delegate type FooFoo += method;
: adds a method to the delegateFoo -= method;
: removes a method from the delegateFoo(2, "two");
: invokes a delegate (calls all the methods in it, in the order they were added) with the given argumentslock (object) { ... }
: Synchronizes access to the block of code for object
.object
is usually this
.The following macros are contained in the Nemerle.Concurrency
namespace:
async { ... }
: executes a block of code asynchronouslyasync Read (s : string) { ... }
creates a method that will always execute asynchronouslyA "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 that resource
is correctly disposed of even if an exception is thrown. resource
must implement the IDisposable
interface.unchecked { ... }
: within the unchecked scope numerical operations will silently overflowchecked { ... }
: operations will throw an OverflowException
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 flagslog (VERB, "example", 2)
: calls the logging function, if the compilation flag is set, with the given parameterswhenlogging (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 include foo
[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 macrosExecuteNonQuery ("INSERT INTO employee VALUES (‘John‘, ‘Boo‘)", connection);
: executes a query returning no results, via the specified connectiondef 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 variablesExecuteReaderLoop (
"SELECT * FROM employee WHERE firstname = $myparm",
dbcon,
{
WriteLine($"Name: $firstname $lastname")
}
);
标签:
原文地址:http://www.cnblogs.com/foreachlife/p/4210067.html