Nemerle, C#, F# における匿名型,レコードの比較


Nemerle には匿名型 (anonymous type) があります。 C# の匿名型F# のレコードに相当するものです。 Nemerle の匿名型は, Nemerle ではおなじみのマクロで定義されています。

ちなみに Nemerle にもレコードと呼ばれるマクロ (Record マクロ) があります。Nemerle のレコードは F# のレコードとは異なり,フィールドに対応する引数を持つコンストラクターを生成するものです。 Record マクロと Accessor マクロを組み合わせると F# のレコードに近くなります[A]

Nemerle の匿名型,Nemerle のレコードマクロ, C# の匿名型, F# のレコードはパッと見で同じようなものですが,使い勝手が違います。混乱しないように比較してみました。

Nemerle の匿名型と F# のレコードが似ている点は,等価演算子で値同士で等値比較が容易に行えることです。一方 C# の匿名型は等価演算子では等値比較できず, Equals メソッドを使う必要があります。 Nemerle のレコードマクロは等価比較に関する実装は一切触りません。

// Nemerle
using Nemerle.Extensions;  // 匿名型マクロが定義されている名前空間

def alice = new (Name = "Alice", Age = 23);
def bob = new (Name = "Bob", Age = 45);
alice == bob;  // False
alice == new (Name = "Alice", Age = 23);  // True
// Nemerle
[Record]
class Person
{
   private name : string;
   private age : int;
}

def alice = Person("Alice", 23);
def bob = Person("Bob", 45);
alice == bob;  // False
alice == Person("Alice", 23);  // False, warning N10011
Equals(alice, Person("Alice", 23));  // False
// C#
var alice = new { Name = "Alice", Age = 23 };
var bob = new { Name = "Bob", Age = 45 };
alice == bob;  // False
alice == new { Name = "Alice", Age = 23 };  // False, Nemerle のコンパイラーでコンパイルすると True
Equals(alice, new { Name = "Alice", Age = 23 });  // True
// F#
type Person = { Name : string; Age : int }
let alice = { Name = "Alice"; Age = 23 }
let bob = { Name = "Bob"; Age = 45 }
alice = bob  // False
alice = { Name = "Alice"; Age = 23 }  // True

F# のレコードは等価演算子の他に比較演算子も実装します。一見便利にも見えますが,等値じゃない比較は必要ない気もします。 [2012-06-30 21:00 追記] ソートに便利というコメントをいただきました。フィールドの宣言順にしたがって比較される模様です。詳しく調べていませんが, GenericComparison あたりを使って比較しているのでしょうか。 [2012-07-01 05:15 追記] F# 2.0 Language Specification によると Operators.compare 関数を使うみたいです。

匿名でないレコードはあらかじめ型を定義する必要があるのに対し,匿名型は型が定義されているかどうかということを気にせず気軽に使えます。

F# は同じフィールドを持つ別の型が作成できるというメリットがあります。

// F#
type Human = { Name : string; }
type Animal = { Name : string; }
let hito = { Human.Name = "Hito" }
let inu = { Animal.Name = "Inu" }
// p = a;  // error FS0001

他にも F# では可変フィールドの定義やレコードのコピーおよび更新 (with を使った値の定義) が便利です[B][2012-06-30 21:00 追記] 他にメソッドの定義やオーバーライドも,型を宣言することならではですね。

まとめ

以上をまとめて表にしました。

Nemerle 匿名型 Nemerle レコード C# 匿名型 F# レコード型
等価演算子の実装 △ (手動) × (csc)
○ (ncc)
Equals メソッドの実装 △ (手動)
比較演算子の実装 × △ (手動) ×
型の宣言 不要 必要 不要 必要
フィールド名が同一な別の型 × ×
可変フィールド × ×
メソッドの定義 × ×

更新履歴

  • [2012-06-30 21:00] コメントを反映。メソッドの定義についての項目を追加。その他微修正。
  • [2012-07-01 05:15] F# の比較演算子について追記。

脚注

  1. Accessor マクロを使わずにフィールドを public にするという手もあります。 []
  2. とはいえ, Nemerle でもレコードのコピーおよび更新はマクロで実装できそうですが。 []