Nemerle

Nemerle（ネマール）は.NETプラットフォーム上で動作する静的型付けの高級言語である。 手続き型、オブジェクト指向、関数型言語の機能を取り込んだハイブリッド言語であり、C#によく似た構文構造と強力なメタプログラミング機能が特徴となっている。 Wrocław University（ポーランド）のKamil Skalski、Michał Moskal、Prof. Leszek Pacholski、Paweł Olsztaらによって開発された。 現在ではロシアの開発コミュニティであるRSDNによって開発・保守がされているが、2012年よりJetBrainsがコア開発者を雇用して、"N2" と呼ばれる新規・既存言語を実装するためのフレームワークの開発に注力している。 おそらくNemerleの最も重要な特徴は、オブジェクト指向プログラミングと関数型プログラミングの両方を混在可能であるということであろう。プログラムのトップレベルではオブジェクト指向の構造を用いるが、メソッド本体には関数型のスタイルを用いることもできる。これは幾つかのプログラムにおいて非常に便利に機能する。以下に特徴的な機能を示す。 強い型推論 マクロによる柔軟なメタプログラミング (C#やJava、C++のような文法の)オブジェクト指向プログラミング (ML、OCaml、Haskellのような文法の)関数型プログラミングの諸機能： 高階関数 パターンマッチング 代数的データ型 ローカル関数 タプルと匿名型 関数の部分適用 メタプログラミング機能はコンパイラの高度な拡張を可能としており、プログラマの負担を可能な限り軽減しつつ、ドメイン固有言語の組み込み、部分評価、アスペクト指向プログラミングなどを可能にしている。 また、ジェネリックプログラミングやラムダや拡張メソッドなどといったCLIの全ての機能を利用することができ、.NETのライブラリをC#と同じくらい（あるいはそれ以上に）簡単に扱うことも可能である。 以下では、これらの機能をNemerle中でどのように利用できるかをいくつかの例を挙げて説明する。 def x = 1; // int def myList = List(); // ジェネリック型 List[T] として宣言され、その後の使われ方から推論される myList.Add(x); // コンパイラにより myList は List[int] と推論される def x = { // x = 3 と等しい def y = 1; def z = 2; y + z // 最後に評価された値が返される }; def x = if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) // if、using、try なども全て式である "Monday" else "other day"; def x = try int.Parse(someString) catch { | FormatException() => 0 }; def x = returnBlock : { foreach (i in [1, 2, 3]) when (i > 2) returnBlock(true); // ブロックを抜ける ( x = true となる ) false // x = false }; def k = (1, "one"); // k : (int * string) def (a, b) = k; // a = 1, b = "one" def result = match (number) { | 0 => "zero" | 1 => "one" | x when x < 0 => "negative" | _ => "more than one" } その他のパターンマッチングの例 変数パターン（値を変数に束縛する）: def check (o : object) { match (o) { | i is int => $"An int: $i" | s is string => $"A string: $(s.ToUpper())" | _ => "Object of another type" } } タプルに対するマッチ: match (tuple) { | ( 42, _ ) => "42 on first position" | ( _, 42 ) => "42 on second position" | ( x, y ) => $"( $x, $y )" } 正規表現によるマッチ: using Nemerle.Text; 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"); } using System.Console; // クラスとモジュール(静的クラス)を名前空間として使用できる def next(x) { x + 1 }; // 引数 x の型は使われ方から推論される def mult(x, y) { x * y }; def fibonacci(i) { | 0 => 0 | 1 => 1 | other => fibonacci(i - 1) + fibonacci(i - 2) }; WriteLine(next(9)); // 10 "Console.WriteLine(next(9));" と等しい WriteLine(mult(2, 2)); // 4 WriteLine(fibonacci(10)); // 55 バリアント (代数的データ型とも) は複数の種類の値を表現できる: variant RgbColor{ | Red | Yellow | Green | Different { red : float; green : float; blue : float; } } Nemerle のマクロ機能は、コンパイル中にプログラム中のコードの生成・解析・編集を可能にする。マクロは通常のメソッド呼び出しとして使えるほか、構文を定義して用いることもできる。Nemerleの多くの構文(if、for、foreach、while、usingなど)はマクロによって定義されている。 "if" マクロの例: macro @if (cond, e1, e2) syntax ("if", "(", cond, ")", e1, Optional (";"), "else", e2) { /* <[ ]> はコードクォートであり、Nemerleコンパイラは中身を構文木に変換して提供する。 C# における式木に似ているところがある。 */ <[ match ($cond : bool) { | true => $e1 | _ => $e2 } ]> } // コード中でマクロを使う: def max = if (a > b) a else b; // コンパイル中に上のコードは下のように展開される: def max = match (a > b) { | true => a | _ => b } NemerleはVisual Studio 2008に統合することができる。Visual Studio 2008 Shellをベースとした無料のIDEも存在する。 また、SharpDevelop用のプラグインも用意されている(プラグインのページ)。 アドインによりVisual Studio 2010にも統合が可能で、Visual Studio 2013版も存在している。 伝統的なHello worldは、次のようにC#ライクなスタイルで書くことができる。 class Hello { static Main () : void { System.Console.WriteLine ("Hello, world!"); } } あるいは、よりシンプルに System.Console.WriteLine("Hello, world!"); とも書ける。 $ 記号により文字列中に値を展開することができる: def s = $"The number is $i"; // $i を変数 i の値で置き換える def s = $"$x + $y = $(x+y)"; // $(...) を使うことで複雑な式も展開できる StructuralEquality、Memoize、json、withなどはコンパイル時にコードを生成するマクロである。 いくつかのマクロ(StructuralEquality, Memoize)はC#における属性のように見えるが、コンパイル時に適切なコードへの変換を行う。 [StructuralEquality] // IEquatable[Sample] インターフェースを自動で実装するマクロ class Sample { [Memoize] // 最初の計算結果を保持する public static SomeLongEvaluations() : int { MathLib.CalculateNthPrime(10000000) } [DependencyProperty] // WPF 依存関係プロパティ public DependencyPropertySample { get; set; } public static Main() : void { /* 構文マクロ "json" は次のようなコードを生成する: JObject.Object([("a", JValue.Number(SomeLongEvaluations())), ("b", JValue.Number(SomeLongEvaluations() + 1))]) */ def jObject = json { a: SomeLongEvaluations(); b: (SomeLongEvaluations() + 1)} // オブジェクト初期化マクロ "<-" はC#のオブジェクト初期化子と同じ機能を提供する def k = Diagnostics.Process() <- { StartInfo <- // コンストラクタ呼び出しなしに内部のプロパティの初期化を行える { FileName = "calc.exe"; UseShellExecute = true; } Exited += () => WriteLine("Calc done"); // イベントとデリゲート } ReadLine(); } } 例えば、Nemerleのマクロを用いてSQL文を発行するコードは以下のように書くことができる。 ExecuteReaderLoop ("SELECT firstname, lastname FROM employee WHERE firstname = $myparm", dbcon, { System.Console.WriteLine ("Name: {0} {1}", firstname, lastname) }); これは、次の文の代わりに用いられる。 string sql = "SELECT firstname, lastname FROM employee WHERE firstname = :a"; NpgsqlCommand dbcmd = new NpgsqlCommand (sql, dbcon, dbtran); dbcmd.Parameters.Add("a", myparm); NpgsqlReader reader = dbcmd.ExecuteReader(); while(reader.Read()) { string firstname = reader.GetString (0); string lastname = reader.GetString (1); System.Console.WriteLine ("Name: {0} {1}", firstname, lastname) } reader.Close(); dbcmd.Dispose(); これは、ライブラリ中の幾つかの動作を隠すだけでなく、クエリ文字列や変数、データベースから返されたコラムがコンパイラによって理解され実行されるということを表している。ExecuteReaderLoopマクロは、プログラマが記述しなければならないコードと同等のコードを生成させることができる。またSQL文が正しいかどうかをコンパイル時に検査することも可能である。 Nemerleのマクロを用いると、言語に新しい構文を導入することも可能である。 macro ReverseFor (i, begin, body) syntax ("ford", "(", i, ";", begin, ")", body) { <[ for ($i = $begin; $i >= 0; $i--) $body ]> } この例ではford ( EXPR ; EXPR ) EXPR構文を定義していて、この構文は次のように用いることができる。 ford (i ; n) print (i); NemerleはASP.NET上に直接組み込むことが可能である。 <%@ Page Language="Nemerle" %> <script runat="server"> Page_Load(_ : object, _ : EventArgs) : void { Message.Text = $"You last accessed this page at: $(DateTime.Now)"; } EnterBtn_Click(_ : object, _ : EventArgs) : void { Message.Text = $"Hi $(Name.Text), welcome to ASP.NET!"; } </script> <html> <body> <form runat="server"> Please enter your name: <asp:TextBox ID="Name" runat="server" /> <asp:Button OnClick="EnterBtn_Click" Text="Enter" runat="server" /> <p><asp:Label ID="Message" runat="server" /></p> </form> </body> </html> また、別ファイルにコードを記載し、それを呼び出すことも可能である。 <%@ Page Language="Nemerle" Src="test.n" Inherits="Test" %> Nemerleはネイティブのプラットフォームライブラリを呼び出すことができる。その構文はC#やその他の.NET言語に非常に似ている。 using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public extern static puts(c : string) : int; [DllImport("msvcrt.dll")] internal extern static _flushall() : int; public static Main() : void { _ = puts("Test"); _ = _flushall(); } }