CsharpDiff
From Nemerle Homepage
Contents |
Introduction
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.
It is meant as a reference for people already programming in Nemerle, not as a tutorial. For hints about learning Nemerle please consult the documentation.
Most changes (vs C#) are dictated by features, so please check them out first before judging about anything here :-)
There are also some minor differences, that didn't fit into this table.
New stuff
This section talks about the 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 a new name. |
class Foo { static readonly int x; static int m () { ... } } |
module Foo { x : int; m () : int { ... } } | module is a class that has all members static. |
There are other things, that don't fit here very well (this document
should be a short list of differences), so we just give some links.
- Tuples -- nameless, heterogeneous data structure.
- List literals -- shortcut syntax for lists.
- The void literal -- strange thing.
- Named parameters -- naming parameters in method calls.
- Local functions -- defining functions within other functions.
- Functional values -- passing functions as parameters and returning them from other functions.
- Lambda expression -- defining nameless functions.
- Variants and matching -- a big topic about special Nemerle data structure.
- Macros -- powerful but safe code generating macros.
Changes in expressions
| 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 once defined. Immutable values have no direct equivalent in C# or Java. |
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. Mutable values behave like variables do in imperative languages. |
expr_1 = expr_2 = expr_3; |
def tmp = expr_3; expr_2 = tmp; expr_1 = tmp; | The type of the assignment operator is void. |
cond ? expr_1 : expr_2; |
if (cond) expr_1 else expr_2 | No ternary operator, but everything is an expression. 'else' branch is mandatory here! |
new Class (parms) |
Class (parms) | The new keyword for creating an object is dropped. |
new Class [size] |
array (size) | If the type can be inferred from context (which is most of the time), you can drop the declaration. |
new Class [size] |
array (size) : array [Class] | If the type cannot be inferred, you must declare it. |
new Type[] { expr_0, expr_1, ..., expr_n } |
array [expr_0, expr_1, ..., expr_n] | The array constructor. |
new { Property1 = 1; Property2 = "string" } |
new ( Property1 = 1; Property2 = "string" ) | Nemerle anonymous types are more flexible (e. g. can be generic or returned from a method), but have somewhat different syntax. |
new Class { Property1 = 1; Property2 = "string" } |
Class() <- { Property1 = 1; Property2 = "string" } | Nemerle object modifier macro is more powerful. |
if (cond) return foo; do_something (); return bar; |
if (cond) foo else { do_something (); bar } | There is no return statement that cuts control flow in Nemerle. |
if (cond) answer = 42; ... |
when (cond) answer = 42; ... | if without else is called when. This optional short form is provided because Nemerle requires if statements to be paired with else. |
if (!cond) answer = 42; ... |
unless (cond) answer = 42; // or: when (!cond) answer = 42; ... | if without "then" is called unless. Like when, nobody is forced to use it. |
try {...} catch (FooException e) { ... } catch (BarException e) { ... } |
try {...} catch { | e is FooException => ... | e is BarException => ... } | Nemerle's somewhat different try ... catch syntax is consistent with its match structure. |
(type) expr |
expr :> type | Runtime type cast, allows for downcasts and upcasts. |
(type) expr |
expr : type | Static cast, allows only upcast. |
using System; using SWF = System.Windows.Forms; using System.Xml; ... Console.WriteLine ("foo"); SWF.Form x = new SWF.Form (); XmlDocument doc = new XmlDocument (); |
using System; using System.Console; using SWF = System.Windows.Forms; ... WriteLine ("foo"); def x = SWF.Form (); def doc = Xml.XmlDocument (); |
In Nemerle, you can apply the using directive to classes as well as namespaces. Opened namespaces allow you to cut the prefix of other namespaces, like System in System.Xml. More info. |
using System.Windows.Forms; Button button = control as Button; if (button != null) ... else ... |
match (control) { | button is Button => ... | listv is ListView => ... | _ => ... // null case } |
Expressions returning null if a variable is of a given type must be simulated with match. It is a little bit longer construct, but pattern matching is a more general and powerful feature for most of the tasks where it would be used. |
int y = x++;
++x;
|
def y = x;
x++;
++x;
| The ++ and -- operators return void, just like assignment. So, both prefix and postfix versions are equivalent. |
Changes in type definitions
| C# | Nemerle | Remarks |
|---|---|---|
static int foo (int x, string y) { ... } |
static foo (x : int, y : string) : int { ... } | Types are written after variable names. |
class Foo { public Foo (int x) { ... } } |
class Foo { public this (x : int) { ... } } | The constructor's name is always this. |
class Foo { ~Foo () { ... } } |
class Foo { protected override Finalize () : void { ... } } | There is no special syntax for the destructor, you just override the Finalize method. |
class Foo : Bar { public Foo (int x) : base (x) { ... } } |
class Foo : Bar { public this (x : int) { base (x); ... } } | The base constructor is called in the constructor's function body. |
class Foo { int x; } |
class Foo { mutable x : int; } | Fields to be changed outside the constructor need to be marked mutable. |
class Foo { readonly int x; } |
class Foo { x : int; } | Read-only is the default. |
class Foo { const int x = 1; } |
class Foo { static x : int = 1; } | Static literal is const. |
class Foo { const int x = 1; const string y = "a"; } |
class Foo { x = 1; y = "a"; } | Integral literal and string literal are const. |
class Foo { static int x = 1; } |
class Foo { static mutable x : int = 1; } | Static variable. |
class A { public A() { } } class Foo { static readonly A x = new A(); } |
class A { public this() { } } class Foo { static x : A = A(); } | Read-only is the default. |
class Foo { static readonly int x = 1; } |
class Foo { static x : int = 1; // const } | Compiler itself determines whether a static member is const or static readonly. |
class C : I1, I2 { void I1.m () { ... } void I2.m () { ... } } |
class C : I1, I2 { I1_m () : void implements I1.m { ... } I2_m () : void implements I2.m { ... } } | When two interfaces have methods which share a name, explicit names are required when implementing them. |
using System.Runtime.CompilerServices.CSharp; class C { public object this [int i] { ... } [IndexerName("MyItem")] public int this [string name] { ... } } |
class C { public Item [i : int] : object { ... } public MyItem [name : string] : int { ... } } | Nemerle allows defining named indexers in an easier way, just like in VB.NET. |
Generics in C#...
The syntax and semantics of parametric polymorphism in Nemerle is the same as what is called generics in C# 2.0. You will see a naming convention of prefixing type variables with an ', like 't. You need not obey it, though.
| C# | Nemerle | Remarks |
|---|---|---|
class A <T> { readonly T x; } |
class A ['t] { x : 't; } | Type parameters are written in square brackets [...]. |
class A <T> where T : IComparable <T> { ... } |
class A ['t] where 't : IComparable ['t] { ... } | |
class A <T> where T : IFoo, IBar { ... } |
class A ['t] where 't : IFoo, IBar { ... } | |
class A <X,Y> where X : IFoo, IBar where Y : IBaz { ... } |
class A ['x,Y] where 'x : IFoo, IBar where Y : IBaz { ... } | Multiple type variables. Using ' is optional, but it is a common naming convention in functional programming. |
typeof(A<,>); |
typeof(A[_,_]); | typeof expression |
int meth<A> (A x) { ... } |
meth ['a] (x : 'a) : int { ... } | Polymorphic method. |
int meth<A> (A x) where A : IFoo { ... } |
meth ['a] (x : 'a) : int where 'a : IFoo { ... } | Polymorphic method with constraints. |
The same (mostly)
This section lists things that are written (mostly) the same way as they are in C#. It only includes cases when it could be doubtful.
| C# | Nemerle | Remarks | |
|---|---|---|---|
// A comment. /* A possibly multiline comment. */ |
// A comment. /* A possibly multiline comment. */ | ||
throw new System.ArgumentException ("foo"); |
throw System.ArgumentException ("foo") | throw is the same (but new is dropped). | |
@"foo\bar" |
@"foo\bar" | Quoted string literals. | |
using System; Console.WriteLine("Hello C#."); |
using System.Console; WriteLine("Hello Nemerle."); | In Nemerle the using directive can be applied to classes as well as namespaces. | |
try { foo (); bar (); } catch (Exception e) { baz (); } finally { qux (); } |
try { foo (); bar () } catch { e => baz () } finally { qux () } | Simple try ... catch structures are nearly the same (note the match syntax in catch). Also, semi-colons are optional at the end of code blocks. | |
interface IA { int I { get; } int I2 { get; } // Must write ';' int J { get; set; } int J2 { get; set; } // Must write ';' } |
interface IA { I : int { get; } I2 : int { get } // Skip last ';' J : int { get; set; } J2 : int { get; set } // Skip last ';' } |
Nemerle doesn't require last ';' in block. C# doesn't compile without it. |
Other minor differences
There are some minor differences, mainly on the semantics level.