Nemerle入門Ⅳ ～tuple，バリアント，もっとパターンマッチ～

# Tuple
C\# 7でやっと使いやすくなった`Tuple`ですが，Nemerleは以前より使いやすいです．
Listと同じく，Nemerle独自の`Tuple`型(`Nemerle.Builtin.Tuple`)で説明していきます．  
Nemerleの`Tuple`は標準で構造体です．

要素へのアクセスは，`Item1, Item2 ...`ではなく，インデクサでアクセスします．

```nemerle:tuple_access
def two_pair = (1,2);
WriteLine($"first=$(two_pair[0]), second=$(two_pair[1])");
```

Nemerleのタプルは，個々のデータは変更できません．`readonly`になっています．

```nemerle:tuple_cannnot_change_data
mutable two_pair = (1,2);
two_pair[0] = 2; // Compilation Error!
```
関数に引数として渡すときにタプルを渡すと自動的に分解されます．

```nemerle:tuple_with_functions
def Add(x : int, y) { x + y };
def tup = (1, 2);
Add(tup) |> WriteLine;
```

# Variant
バリアントは直和型です．**VisualBasicのVariantとは全く違います．**
まずは基本形．ディスプレイサイズに関係するEnumを作ろうとします．

```nemerle:variant_1
variant DisplaySize {
  | VGA
  | HD
  | FullHD
}
```
まんまEnumです．ですが，画面のサイズの種類は多く，これだけでは表せません．そこで，Otherを追加します．

```nemerle:variant_2
variant DisplaySize {
  | VGA
  | HD
  | FullHD
  | Other
}
```
しかし，Otherの時は，縦横のサイズを詳しく取得できません．そんな時に，バリアントが役立ちます．`Other`のときだけ，`Height`と`Width`という値を格納することできます．

```nemerle:variant_3
variant DisplaySize {
  | VGA
  | HD
  | FullHD
  | Other {
     Height : uint;
     Width  : uint;
    }
}
```
バリアントは実際に，以下のように使います．

```nemerle:variant_using
def MainDisplay = DisplaySize.HD();
def SubDisplay = DisplaySize.Other(800, 1280);
```
IL的に，バリアントは次のような構造になります．

```csharp:variant_in_OPP
class DisplaySize {
  class VGA : DisplaySize {}
  class HD : DisplaySize {}
  class FullHD : DisplaySize {}
  class Other : DisplaySize {
    uint Height;
    uint Wdith;
    DisplaySize(uint _height, uint _width) {
      Height = _height;
      Width = _width;
    }
  }
}
```

# もっとパターンマッチ
実践編などで，「もっともっとパターンマッチ」を入れようと考えていますが，今回は，Nemerleのパターンマッチの機能の全貌を紹介します．
## tuple
基本的には`list`と同じようにします．長さの違うタプルは駄目です．

```nemerle:tuple_patternmatch
def t = (1, 2);
match(t) {
  | (1, _) => //Do Something
  | (1, 2) => //Do Something
  | _ => //Do Something if there aren't any matched condition.
  | (1, 2, 3) => // Compilation ERROR
}
```
## バリアント
さっきの`DisplaySize`があるとしてパターンマッチングします．

```nemerle:variant_patternmatch
def s = DisplaySize.FullHD();
match(s) {
   | DisplaySize.VGA => //Do Something
   | DisplaySize.HD => //Do Something
   | DisplaySize.FullHD => //Do Something
   | DisplaySize.Other(Height = h, Width = w) =>
         WriteLine($"Your display size is h:$h w:$w");
   | DisplaySize.Other(_, 1280) as ss =>
         WriteLine(ss.Height);
```
値を持たない時は，そのまま，値を持つときは，上の例のように取り出すことができます．
其の時に，`_`で何でも許容することもできます．`as ss`はこの後紹介します．

## 型パターン/型スイッチ
Scala / C# 7.1のように，型パターン / 型スイッチすることができます．

```nemerle:patternmatch_typecheck
def o : object = 12;
match (o) {
  | i is int => WriteLine($"integer:$(i + 12)"); 
  | str is string =>  WriteLine("text:" + str);
  | _ => WriteLine("Unknown!");
}
```
```csharp:C#でtypeswitch
object o = 12;
switch(o) {
  case int i: WriteLine($"integer:{i + 12}"); break;
  case string str: WriteLine("text:" + str); break;
  default: WriteLine("Unknown!"); break;
}
```
```scala:Scalaでtypepattern
val o : object = 12
o match {
  case i : int => println(s"integer:${i + 12}")
  case str : string => println("text:" + str)
  case _ => println("Unknown!")
}
```

## レコードパターン
何重にもif文を重ねなくてもいいようになります．

```nemerle:patternmatch_record
class hoge {
  public hogeint : int;
  public hogestr : string;
}

def hogeMatch(h : hoge) {
  match(hoge) {
    | (hogeint = 1, hogestr = "HOGEHOGE") => //DoSomething
    | (hogeint = 2, hogestr = "HAGEHAGE") => //DOSomething
    | _ => //DoElse
  }
}
```

## as
`as <変数名>`で，パターンマッチングした変数を`<変数名>`で扱うことができます．

```nemerle:patternmatch_as
def someFunc() { (12, 2) }
match(someFunc()) {
  | (_, 2) as t => WriteLine($"$(t[0])";
  | _ => //DoSomething
}
```
変数の定義文が減って，コードの視認性が上がると思います．

## x::xsでの型指定
```nemerle:x_xs_select_type
match(["aaa", "bbb"]) {
  | (x : string) :: xs => //DoSomething
}
```
こんなこともできます．

## 複数条件
```nemerle:multi_condition
match (my_list) {
  | [1]
  | [1, 2] => WriteLine ("one or one, two");
  
  | [x]
  | [7, x] => WriteLine ($ "$x or seven, $x");

  | _ => //Else
}
```
以下の様なことはできません．(`x`が定義されない場合があるため)

```nemerle:Error_multicondition
| [1]
| [1, x] => WriteLine($"one, $x");
```

## `match`の省略
以下の様な場合，matchを省略できます．

```nemerle:non_write_match
def some(n : list[int]) {
  match(n) {
    | 3 :: _ => //DoSomething
    | [5, _] => //DoSomething
    | _ => //DoSomething
  }
}

            ↓

def some(_) {
  | 3 :: _ => //DoSomething
  | [5, _] => //DoSomething
  | _ => //DoSomething
}
```

この場合，`some`は引数に`list[int]`を取ることになります．

## with
言葉で説明するより見たほうが早いでしょう．

```nemerle:with_patternmatch
def foo (_) {
  | [x] with y = 3
  | [x, y] => x * y
  | _ => 42
}

//foo (3) = 9
//foo (3, 4) = 12
//foo (3, 4, 5) = 42
```
2行目で，もし，fooに長さ1のlistが渡された場合，2つ目の要素(3行目に定義されているy)を3として，3行目のコードに渡します．このコードは以下のコードと同じです．

```nemerle:with
def foo (_) {
  | [x] => foo_background(x, 3)
  | [x, y] => foo_background(x, y)
  | _ => 42
}
def foo_go(x : int, y) {
  x * y;
}
```

# 〆
[入門Ⅴへ](http://qiita.com/Gnico/items/6168e9aefcf83b5096b0)