タプルってまぎらわしい。何がまぎらわしいって...

F#はMicrosoftの[Don Syme](https://twitter.com/dsyme "https://twitter.com/dsyme")氏が開発したプログラミング言語で、命令型、オブジェクト指向、関数型の要素を併せ持つマルチパラダイム言語と[位置付けられています](https://msdn.microsoft.com/visualfsharpdocs/conceptual/visual-fsharp "https://msdn.microsoft.com/visualfsharpdocs/conceptual/visual-fsharp")。実装はオープンソースの[MITライセンス](https://github.com/fsharp/fsharp/blob/master/License.txt "https://github.com/fsharp/fsharp/blob/master/License.txt")のもと、[githubのリポジトリ](https://github.com/fsharp/fsharp "https://github.com/fsharp/fsharp")で公開されています。

実行環境は.NET Frameworkおよび.NET CoreやMonoといったマルチプラットフォームに対応していますが、サポート状況については各プラットフォームごとの情報を参照してください(「[F# の概要](https://docs.microsoft.com/ja-jp/dotnet/fsharp/get-started/index "https://docs.microsoft.com/ja-jp/dotnet/fsharp/get-started/index")」など)。

なお、ここで対象とするF#のバージョンは4.1([FSharp.Core](https://github.com/fsharp/fsharp/tree/master/src/fsharp/FSharp.Core "https://github.com/fsharp/fsharp/tree/master/src/fsharp/FSharp.Core") 4.4.1.0)とします。

# タプルってまぎらわしい
[タプル](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/tuples "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/tuples")は型が異なる複数の値を1つの組として同時に扱える型のことで`(値, 値, ...)`と表します。ただ、このタプルがいろいろとまぎらわしいかったりします。

## 2つのタプル型
F# 4.1では.NET Framework 4.5 / .NET Standard 1.0から提供されてきた[System.Tuple](https://docs.microsoft.com/ja-jp/dotnet/api/system.tuple?view=netframework-4.7.1 "https://docs.microsoft.com/ja-jp/dotnet/api/system.tuple?view=netframework-4.7.1")(クラスのタプル)と.NET Framework 4.7 / .NET Standard 2.0から新たに提供される[System.ValueTuple](https://docs.microsoft.com/ja-jp/dotnet/api/system.valuetuple?view=netframework-4.7.1 "https://docs.microsoft.com/ja-jp/dotnet/api/system.valuetuple?view=netframework-4.7.1")(構造体のタプル)を扱えます。

`System.Tuple`は`(1, "uno", obj())`のように表し、型名は`int * string * obj`のように型名を`*`で区切って示します。`System.ValueTuple`は`struct (1, "uno", obj())`のように表し、型名は`struct (int * string * obj)`のように全体を`struct (...)`で囲んで示します。

```fsharp:2つのタプル型がある
(1, "uno") = System.Tuple.Create(1, "uno")
struct (1, "uno") = System.ValueTuple.Create(1, "uno")
```

両者は[System.TupleExtensionsクラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.tupleextensions?view=netframework-4.7.1 "https://docs.microsoft.com/ja-jp/dotnet/api/system.tupleextensions?view=netframework-4.7.1")のメソッドを使って相互に変換できます。

```fsharp:System.TupleExtensionsクラスによるタプル型の相互変換
// TupleからValueTupleへの変換
System.TupleExtensions.ToValueTuple((1, "uno")) = struct (1, "uno")
// ValueTupleからTupleへの変換
System.TupleExtensions.ToTuple(struct(1, "uno")) = (1, "uno")
```

## リストや配列の要素をカンマで区切ると...
F#では、リストや配列などのコレクションオブジェクトでは、要素を`;`(セミコロン)で区切ります。しかし、誤ってこれらを','で区切るとコレクションの中に1つのタプルだけが存在しているとみなされます。エラーにはなりませんので、まぎらわしく思うときがあるかもしれません。

```fsharp:要素をカンマで区切ると...
(* 本来のリスト *)
[1; 2; 3]  // int list
[1, 2, 3]  // (int * int * int) list    [(1, 2, 3)]と同じ

[|1; 2; 3|]  // int []
[|1, 2, 3|]  // (int * int * int) []    [|(1, 2, 3)|]と同じ
```

## 型名が複雑になると...
タプルの型名は複数の型名を`*`で区切ったものですが、タプルがネストしたときや`option`, `list`などが組み合わされたときはカッコがつくかどうかで階層関係が変わります。データ構造を誤解しないように注意が必要です。

```fsharp:タプルのネスト
(1, "uno", (2, "dos"))  // int * string * (int * string)
(1, (2, "dos"), "uno")  // int * (int * string) * string
((1, "uno"), 2, "dos")  // (int * string) * int * string
```

```fsharp:別の型との組み合わせ
(1, [2; 3])       // int * int list
[(1, 2); (3, 4)]  // (int * int) list

(1, Some "uno")  // int * string option
Some (1, "uno")  // (int * string) option

(1, ["uno"; "dos"])       // int * string list
[(1, "uno"); (2, "dos")]  // (int * string) list

(1, Some ["uno"; "dos"])       // int * string list option
(1, [Some "uno"; Some "dos"])  // int * string option list

Some [(1, "uno"); (2, "dos")]       // (int * string) list option
[Some (1, "uno"); Some (2, "dos")]  // (int * string) option list

Some (1, ["uno"; "dos"])            // (int * string list) option
[(1, Some "uno"); (2, Some "dos")]  // (int * string option) list
```

## 引数は個別かタプルか

関数の引数が複数あるとき、それらは個別のものか、それともタプルでくくるのかが、関数の型が変わります。

```fsharp:引数は個別かタプルか
let f x y    = x + y  // int -> int -> int
let g (x, y) = x + y  // int * int -> int

f 1 2 = 3
g (1, 2) = 3
```

`let f x y = x + y`と定義される関数`f`の型は`int -> int -> int`です。そして`let g (x, y) = x + y`と定義される関数`g`は`int * int -> int`です。両者の結果は等価ですが、`f`の引数は`int`が2つ、`g`の引数は`int * int`1つで、関数の型は全く異なっています。

そのため、`f`は以下の`f 1`のように引数が部分適用された関数を定義できますが、`g`に対してはこのような関数を定義できません。

```fsharp:引数の定義による違い
let f1 = f 1
f1 2  // 3

let g1 = g 1  // 実行できない(引数の型がgの定義と異なるため)
```

## コンストラクタとメソッドの引数
この違いはコンストラクタやメソッドの型でも見られます。

引数の数が異なるコンストラクタを持つクラスを定義し、それぞれのインスタンスを生成するとき、引数が1つのものは両端に`()`がなくても実行できますが、2つのものは両端に`()`をつけないと実行できません。これはコンストラクタ実行時の引数列が1つのタプルとして扱われているからです。メソッドでもこれと同じ扱いになります。.NETのクラスなどでも同様です。

```fsharp:引数の数と型の違い(コンストラクタ)
(* コンストラクタの定義 *)
type C =
  new () = { ... }        // ()  -> Cインスタンス
  new (a1) = { ... }      // obj -> Cインスタンス
  new (a1, a2) = { ... }  // obj * obj -> Cインスタンス

(* インスタンス生成 *)
C()      // 引数なしを表すカッコ(unit)が必要
C 1      // カッコなしでも実行できる
C(1, 2)  // 引数列は1つのタプル - C 1 2 では実行できない
```

```fsharp:引数の数と型の違い(メソッド)
type C() =
  member this.Mf x y    = x + y
  member this.Mg (x, y) = x + y

C().Mf 1 2     // 3
C().Mg (1, 2)  // 3
```

```fsharp:引数の数と型の違い(.NETのメソッド)
let s = ".NET Framework"
s.replace ("Framework", "Core")  // ".NET Core"
s.replace "Framework" "Core"     // 実行不可
```

## 戻り値がタプルのときがある
.NETのクラスやメソッドを利用するとき、C#でのシグネチャと、F#での型の定義とが異なっている場合があります。

`System.Int32`の[TryParseメソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.int32.tryparse?view=netframework-4.7.1#System_Int32_TryParse_System_String_System_Int32__ "https://docs.microsoft.com/ja-jp/dotnet/api/system.int32.tryparse?view=netframework-4.7.1#System_Int32_TryParse_System_String_System_Int32__")は、C#では戻り値が`bool`で、かつ第2引数の`out`によって変換の結果を得られるのですが、F#では戻り値の型が`bool * int`になっています。つまり、タプルの左の要素は変換の可否を表し、それが`true`であれば右の要素に変換結果が得られていることを表します。

```fsharp:C#のシグネチャとF#の型が異なる
(* System.Int32.TryParseメソッドの場合 *)
C#: public static bool TryParse (string s, out int result)
F#: TryParse: string -> bool * int

(* F#でInt32.TryParseを実行 *)
let atoi s =
  match System.Int32.TryParse s with
  | (true , x) -> x
  | (false, _) -> sprintf "「%s」を数値に変換できませんでした" s
                  |> invalidArg "Int32.TryParse"

atoi "123"  // 123
atoi "abc"  // "System.ArgumentException: 「abc」を数値に変換できませんでした"
```

## 要素が1つだけのタプル
タプルは要素を複数持つと紹介しましたが、実は1つしか要素を持たないタプルもつくれないわけではありません。ただ、つくれたとしても一見するとタプルではない値のようにも見えます。

こうしたタプルの型名は`System.Tuple<'T>`や`System.ValueTuple<'T>`となります。

```fsharp:要素が1つだけのタプル
let t1 = System.Tuple.Create("uno")       // ("uno")
let s1 = System.ValueTuple.Create("uno")  // struct ("uno")

(* 型情報のチェック *)
t1.GetType() = typeof<System.Tuple<string>>       // true
s1.GetType() = typeof<System.ValueTuple<string>>  // true
```

## Item1, Item2, ...にアクセスできない
2つのタプル型はいずれも.NET環境で定義されたクラスと構造体で、個々のタプルはそれらのインスタンスで表されます。両者には、各要素の値を個別に得られる`Item1`, `Item2`, ...というプロパティ(あるいはフィールド)が提供されます。ですが、F#ではこれらにアクセスできません(対話型環境[fsi](https://docs.microsoft.com/ja-jp/dotnet/fsharp/tutorials/fsharp-interactive/ "https://docs.microsoft.com/ja-jp/dotnet/fsharp/tutorials/fsharp-interactive/")にて確認)。

```fsharp:タプル型のプロパティやフィールドにアクセスできない
(1, "uno").Item1           // アクセスできない
(struct (1, "uno")).Item1  // アクセスできない
```

ただ、これらのプロパティを使わなくても、それぞれの要素を個別に代入したければ、以下のようにできます。

```fsharp:タプルの要素を個別に代入
let (a, b, c)        = (1, "uno", obj())  // a = 1; b = "uno"; c = obj()
let struct (a, b, c) = struct (1, "uno", obj())  // 同上

(* mutableな変数にも代入可 *)
let mutable (a, b, c) = (1, "uno", obj())
let mutable struct (a, b, c)  = struct (1, "uno", obj())
```

また、要素が2つのタプルに対しては、左の要素にアクセスする`fst`、右の要素にアクセスする`snd`という関数が提供されます。これらは型情報もそのまま残ります。ただし`Tuple`にしか対応しません。

```fsharp:fst,snd関数はTupleにのみ対応
fst (1, "uno") = 1
snd (1, "uno") = "uno"
```

`ValueTuple`に対応する関数はすぐにつくれます。

```fsharp:ValueTypeに対応するfst,snd相当の関数
let fsts struct (a, _) = a  // fsts struct (1, "uno") = 1
let snds struct (_, b) = b  // snds struct (1, "uno") = "uno"
```

要素が少なければこれで良いのですが、要素の数が多くなると、これでは対応できなくなるでしょう。`fst`, `snd`に相当する関数をタプルの要素の数だけ定義するのも面倒です。

そんなときはF#のリフレクション関数[FSharpValue.GetTupleField](https://msdn.microsoft.com/visualfsharpdocs/conceptual/fsharpvalue.gettuplefield-method-%5bfsharp%5d "https://msdn.microsoft.com/visualfsharpdocs/conceptual/fsharpvalue.gettuplefield-method-%5bfsharp%5d")で指定した位置の要素を得られます。この値は`obj(System.Object)`型になっていますので、適宜`unbox<'T>`を実行してください。この関数はどちらのタプル型にも対応します。

```fsharp:F#のリフレクション関数で指定した位置の要素を得る
open Microsoft.FSharp.Reflection

let t = (1, "uno")
let s = struct (1, "uno")

FSharpValue.GetTupleField(t, 0) |> unbox<int>     // 1
FSharpValue.GetTupleField(s, 1) |> unbox<string>  // "uno"
```

もちろん、`Item1, Item2, ...`などにアクセスする方法がないわけではありません。.NETのリフレクションAPIを使えば良いのです。こちらも適宜`unbox<'T>`を実行して元の型に戻してください。

```fsharp:.NETのリフレクションAPIによるプロパティやフィールドへのアクセス
(* =は等価の判定 *)
let t = (1, "uno")
t.GetType().GetProperty("Item1").GetValue(t) |> unbox<int>  // 1

let s = struct (1, "uno")
s.GetType().GetField("Item2").GetValue(s) |> unbox<string>  // "uno"
```

## [おまけ1] 配列とタプルの変換
タプルと`obj[]`型(`obj`型の配列)との間で変換を行う関数があります。関数はいずれも[Microsoft.FSharp.Reflectionモジュール](https://msdn.microsoft.com/visualfsharpdocs/conceptual/microsoft.fsharp.reflection-namespace-%5bfsharp%5d "https://msdn.microsoft.com/visualfsharpdocs/conceptual/microsoft.fsharp.reflection-namespace-%5bfsharp%5d")で提供されます。

現状、タプルから`obj[]`型への変換は`Tuple`と`ValueTuple`のどちらも対応していますが、その逆は`Tuple`のみの対応です。`ValueType`のタプルを得るには、さらに変換が必要です。もしかすると今後のバージョンアップで対応するかもしれません。

```fsharp:タプルとobj[]型との変換
open Microsoft.FSharp.Reflection

(* タプルからobj[]への変換 *)
FSharpValue.GetTupleFields (1, "uno")  // [|box 1; box "uno"|]
FSharpValue.GetTupleFields struct(1, "uno")  // [|box 1; box "uno"|]

(* obj[]からタプルへの変換 *)
FSharpValue.MakeTuple([|1; "uno"|], typeof<int * string>)
|> unbox<int * string> // (1, "uno")

FSharpValue.MakeTuple([|1; "uno"|], typeof<int * string>)
|> unbox<int * string>
|> System.TupleExtensions.ToValueTuple  // struct (1, "uno")
```

## [おまけ2] リストとタプルの変換
必要性はともかくとして、リストとタプルの間で変換する関数もつくってみました。これらも[Microsoft.FSharp.Reflectionモジュール](https://msdn.microsoft.com/visualfsharpdocs/conceptual/microsoft.fsharp.reflection-namespace-%5bfsharp%5d "https://msdn.microsoft.com/visualfsharpdocs/conceptual/microsoft.fsharp.reflection-namespace-%5bfsharp%5d")の関数でつくっています。

```fsharp:リストからタプルへの変換
open Microsoft.FSharp.Reflection

(* リストからタプルに変換する関数 *)
let listToTuple (list: 'T list) =

  // listからobj[]に変換
  let arr = list
            |> List.map (fun item -> box item)
            |> List.toArray

  // 変換するタプルのType
  let typ = list
            |> List.length
            |> Array.replicate
            <| typeof<'T>
            |> FSharpType.MakeTupleType

  // obj[]からTupleに変換
  FSharpValue.MakeTuple (arr, typ)

listToTuple [1; 2]  // (1, 2)
```

```fsharp:タプルからリストへの変換
open Microsoft.FSharp.Reflection

let tupleToList<'T> (t: obj): 'T list =
  t
  |> FSharpValue.GetTupleFields
  |> Array.map (fun item -> item |> unbox<'T>)
  |> Array.toList

tupleToList<int> (1, 2)  // [1; 2]
```

---
ここをご覧いただいた方々の参考になればと思います。