|
This document lists basic differences between Nemerle and C# in a terse form. If you know Java or C++ you also should be able to use it.
C# | Nemerle | Remarks |
---|---|---|
int x = 3; string y = "foo"; FooBarQux fbq = make_fbq (); |
def x = 3; def y = "foo"; def fbq = make_fbq (); | The values of x, y and fbq cannot be changed. |
int x = 3; string y = "foo"; FooBarQux fbq = make_fbq(); |
mutable x <- 3; mutable y <- "foo"; mutable fbq <- make_fbq(); | The values of x, y and fbq can be changed. |
= ; | <- ; | The assignment operator. |
= = ; |
def tmp = <- tmp; <- tmp; | ;The type of assignment operator is void. |
new | ( )( ) | The new keyword is dropped. |
new | [ ]array ( | )If the type can be inferred (most of the time). |
new | [ ](array ( | ) : array ( ))If the type cannot be inferred. |
{ | , , ..., }array [ | , , ..., ]The array constructor. |
if ( ; return ; | ) return ;
if ( else { ; } | ) There is no return statement that cuts control flow in Nemerle. |
if ( ... | ) answer = 42;
when ( ... | ) answer <- 42;The if without else is called when. |
if (! ... | ) answer = 42;
unless ( // or: when (! ) answer <- 42; ... | ) answer <- 42;The if without "then" is called unless. However nobody is forced to use it. |
try ... catch (FooException e) { ... } catch (BarException e) { ... } |
try ... catch { | e : FooException => ... | e : BarException => ... } | Somewhat different syntax, consistent with match though. |
(( | ) )( | :> )Runtime type cast, allows for downcasts and upcasts. |
(( | ) )( | : )Static cast, allow only upcast. |
using System; ... Console.WriteLine ("foo"); |
using System.Console; ... WriteLine ("foo"); | In Nemerle you can also apply using directive to classes. |
while (x < 7) { y += x; x++; } System.Console.WriteLine (y); |
while (x < 7) { y <- y + x; x <- x + 1; }; System.Console.WriteLine (y); | Note semicolon after while {...}. [[FIXME: shouldn't we have ++ += etc?]] |
for (int x = 0; x < 7; x++) { y += x; } System.Console.WriteLine (y); |
for (mutable x <- 0; x < 7; y <- y + x) { x <- x + 1; }; System.Console.WriteLine (y); | Note semicolon after for {...}. |
C# | Nemerle | Remarks |
---|---|---|
static int foo (int x, string y) { ... } | static foo (x : int, y : string) : int { ... } | Type are written after variables. |
class Foo { public Foo (int x) { ... } } |
class Foo { public this (x : int) { ... } } | Constructor name is always this. |
class Foo : Bar { public Foo (int x) : base (x) { ... } } |
class Foo : Bar { public this (x : int) { base (x); ... } } | Base constructor is called in function body. |
class Foo { int x; } |
class Foo { mutable x : int; } | Fields to be changed outside constructor need to be marked mutable. |
class Foo { readonly int x; } |
class Foo { x : int; } | readonly is the default. |
class C : I1, I2 { void I1.m () { ... } void I2.m () { ... } } |
class C : I1, I2 { meth1 () : void implements I1.m { ... } meth2 () : void implements I2.m { ... } } | When two interfaces have the method with the same name. |
Syntax and semantics of polymorphism in Nemerle is the same as what is called generics in C# 2.0.
This section talks about stuff more or less absent in C#.
C# | Nemerle | Remarks |
---|---|---|
void m(int _unused1, string _unused2) { ... } | m (_ : int, _ : string) : void { ... } | Upon each use the _ keyword generates new name. |
class Foo { static readonly int x; static int m () { ... } } | module Foo { x : int; m () : int { ... } } | module is a class, that have all members static. |
Example:
namespace SR = System.Reflection; namespace SC = System.Console; ... def name = SR.AssemblyName (); SC.WriteLine (name.CodeBase); |
This example probably wouldn't convince anybody this feature is needed :) However it comes in handy when one has two very similar namespaces (for two SQL providers) and want to use both without writing veeeery long names each time.
Tuples are form of nameless data structures. They are usable when you need to return two or three values from function.
/** Parses time in HH:MM format. */ parse_time (time : string) : int * int { def arr = time.Split (array [':']); (System.Int32.Parse (arr[0]), System.Int32.Parse (arr[1])) } seconds_since_midnight (time : string) : int { def (hours, minutes) = parse_time (time); (hours * 60 + minutes) * 60 } foo () : void { def secs = seconds_since_midnight ("17:42"); ... } |
Another example could be:
// split (3.7) => (3, 0.7) split (x : double) : int * double { def floor = System.Math.Floor (x); (System.Convert.ToInt32 (floor), x - floor) } |
The void literal is written: ().
The void literal is quite tricky thing, since it represents the only value of type void, which judging from the name should be ergh... void. In fact in some other functional languages this type is called unit, but in Nemerle the name comes from System.Void for which the void type is alias for.
In C# the void type is used as return type of functions. It doesn't make much sense to use it elsewhere. It could be needed however if for example you want to use Hashtable <a, b> type as a set representation of let's say strings. You can use Hashtable <string, void> then. And this is the place to use the void value -- when you call set method, you need to pass something as value to set -- and you pass the void value.
You can also use void value to return it from function -- as you remember the return value of function is the last expression in its body. However in most cases the last expression will already have the right void type.
Local functions are functions defined within other functions. For this reason they are also called nested functions.
There are three reasons to defined local functions. First one is not to pollute class namespace. We have the private keyword for that, so this doesn't seem any good reason.
The second one is that local functions can access variables defined in outer function. This allows for somewhat different (better?) code structuring than in C#. You can have several variables and local functions using them defined in a function.
The most important reason for local function is however the fact that you can pass them to other functions so they can be run from there implementing for example iterators for data structures. This is explained in more detail later.
Local functions are defined just like other values with the def keyword.
sum_cubes (v1 : int, v2 : int, v2 : int) : int { def cube (x : int) : int { x * x * x }; cube (v1) + cube (v2) + cube (v3) } |
In almost all cases you can omit return type specification for local functions. In most cases you can also omit parameter specifications (however not in the example above).
Using local functions is one of way of implementing loops in Nemerle.
class Sum { public Main () : void { def sum (x) { if (x <= 0) 0 else x + sum (x - 1) }; System.Console.WriteLine ("Sum of numbers from 20 to 0 is: {0}", sum (20)); } } |
In Nemerle one can pass functions as arguments of other functions, as well as return them as results. This way functions are not worse then any other data types (think about Equal Rights for Functions movement :-)
In C# one have delegates. This concept is quite similar to functional values. However functional values are far more efficient and their types need not be declared before use.
As a first example consider:
// C# delegate int IntFun (int); class M { static int f(int x) { return x * 2; } static int run_delegate_twice(IntFun f, int v) { return f(f(v)); } static void Main () { System.Console.WriteLine("{0}", run_delegate_twice(new IntFun (f), 3)); } } |
// Nemerle class M { static f (x : int) : int { x * 2 } static run_delegate_twice (f : int -> int, v : int) : int { f (f (v)) } static void Main () { System.Console.WriteLine ("{0}", run_delegate_twice (f, 3)) } } |
In this example delegates seem just like function pointers in C. Functional values don't present any better, except maybe shorter syntax. However real power of delegates comes from the fact that one can use methods as delegates (thus effectively embedding the this pointer in delegate). This is much like "functional objects" design pattern in C++. We won't show it here, please refer to C# manual for details.
Still using methods as delegates doesn't show their full power. The funny part begins when we use nested functions as functional values.
class M { static run_twice (f : int -> int, v : int) : int { f (f (v)) } static run_adder (x : int) : void { def f (y : int) : int { x + y }; System.Console.WriteLine ("{0}", run_twice (f, 1)) } static Main () : void { run_adder (1); run_adder (2); } } |
This example prints 3 and 5. Note how x is captured in local function f.
Types of functions taking more then one argument are represented as tuples. For example the following functions:
some_function (arg_1 : int, arg_2 : string, arg_3 : Foo) : float { // ... } |
has type int * string * Foo -> float. Functions that take no arguments pretend to take one argument of type void. That is function:
other_function () : string { ... } |
posses type void -> string.
This feature (in this form) comes from python. When you call a function you can name some of its parameters (which allows putting them in different order then in definition).
frobnicate (foo : int, do_qux : bool, do_baz : bool, do_bar : bool) { // The method body doesn't mean anything ;-) if (do_qux && !do_baz) foo * 2 else if (do_bar) foo * 7 else if (do_baz) foo * 13 else 42 } Main () : void { // Parameters' names can be omitted. def res1 = frobnicate (7, true, false, true); // This is the indented usage -- the first // (main) parameter comes without a name // and the following flags with names def res2 = frobnicate (7, do_qux = true, do_baz = false, do_bar = true); // You can however name every parameter: def res3 = frobnicate (foo = 7, do_qux = true, do_baz = false, do_bar = true); // And permute them: def res3 = frobnicate (do_qux = true, do_bar = true, do_baz = false, foo = 7); // You can also omit names for any number of leading // parameters and permute the trailing ones. def res2 = frobnicate (7, true, do_bar = true, do_baz = false); () } |
The rules behind named parameters are simple:
Named parameters can be used only when names of parameters are known (which basically mean they do not work in conjunction with functional values).
Lambda expression are just syntactic sugar to defined unnamed local functions. Unnamed local functions are useful when you need to pass some function to iterator, so you use it just once.
Lambda expression are defined using the fun keyword, followed by formal parameters, optional return type and function body.
def x = List.Map (fun (x) { 2 * x }, [1, 2, 3]); def y = List.FoldLeft (fun (acc, val : int) { acc + val }, x); assert (y == 12); |
In general:
fun () { } |
Is equivalent to:
{ def tmp () { }; tmp } |
This feature is similar to anonymous delegates in C# 2.0.
List literals are special forms of writing lists. Lists are data structure that is very often used in Nemerle. Often enough to get own syntax. Lists in Nemerle are somewhat different then the ArrayList type in .NET.
Of course you are free to use .NET ArrayList.
Anyway to construct list consisting of given head (first element) and tail (rest of elements, also a list), write:
:: |
To construct list with specified elements write:
[, , ..., ] |
This way you can also construct empty list ([]).
Nemerle has very powerful code-generating macros. They are more akin to Lisp macros then macros found in C preprocessor. We're not going to explain here how to write macros (if you are curious, please see macros.html), but will describe few often used macros.
First of all if, while, for, foreach, when, using, lock, etc are all macros.
Other examples of macros:
Variants (called datatypes or sum types in SML and OCaml) are forms of expressing data that comes in several different kinds. Simplest example of variants are enum types known from C#.
C# | Nemerle | Remarks |
---|---|---|
enum Color { Red, Yellow, Green } |
variant Color { | Red | Yellow | Green } |
However variant options can carry a value.
variant Color { | Red | Yellow | Green | Different { red : float; green : float; blue : float; } } |
So if color isn't neither red, yellow nor green, it can be represented with RGB.
You can think about variants as of union with selector in C. In OO world one can sometimes see modeling variants with sub classing:
// C# class Color { } class Red : Color { } class Green : Color { } class Yellow : Color { } class Different : Color { float red; float green; float blue; } |
Of course you need to write constructor, mark fields public and so on. When you're done, using this kind of stuff is quite hard -- you need to use lots of is expressions.
On the other hand Nemerle provides easy and convenient method of dealing with variants -- pattern matching:
string_of_color (color : Color) : string { match (color) { | Red => "red" | Yellow => "yellow" | Green => "green" | Different (r, g, b) => System.String.Format ("rgb({0},{1},{2})", r, g, b) } } |
The main idea behind patterns is that they match values that look like them. For example Nemerle compiler creates default constructor for the Different variant option. It has following body:
public this (red : float, green : float, blue : float) { this.red <- red; this.green <- green; this.blue <- blue; } |
Therefore constructor call Different (r, g, b) create new variant option instance with specified arguments. The pattern looks the same -- it binds actual values of red, green and blue fields to r, g and b respectively. You can also say it explicitly:
| Different { red = r; green = g; blue = b; } => System.String.Format ("rgb({0},{1},{2})", r, g, b) |
The example above, while simple, isn't best usage of variants. Variants are best at handling tree-like data structures.
variant XmlNode { | Text { payload : string; } | Element { name : string; children : list (XmlNode); } } parse (n : XmlNode) : string { match (n) { | Element ("foo", []) => construct_foo () | Element ("bar", [Text (t)]) => construct_bar (t) | _ => throw InvalidArgumentException ("n") } } |
The [...] things are list patterns, that work much like list literals.
This section lists things that are written (mostly) the same way as they do in C#. It only includes cases when it could be doubtful.