3種類のLOP
F#のLOPは大きく分けて以下の3種類に分類できます。
- Abstract Representation(抽象的表現)
ホスト言語F#を利用して、あるドメインに対して、より見やすく利用価値の高いAPI、つまり内部DSLを作成します。
- Concrete Representation(具体的表現)
他の言語で表されたドメインの問題をF#にロードします。XML設定ファイル、CSVなどを編集ファイルとしてDSLを作成します(外部DSLの使用)。
- Computation representation(計算機的表現)
F#で書かれたコード内の演算表現を独得の機能を果たすように操作するか、もしくは他の言語に変換します。例えば、ワークフローや、クエリーの操作が当てはまります。
今回は上記の3種類のうち、F#によるAbstract Representation(抽象的表現)について紹介します。
Abstract Representation(抽象的表現)
コンパイラ内部ではソースコードはシンタックスツリー(構文木)として表現されます。F#ではそのシンタックスツリーを型に変換できます。それらの型や、効率よいパターンマッチを利用して内部DSLを作成できます。F#のサブセットとしてのDSLが、問題の本質を比較的自然な形で表現できるのが、F#の抽象的表現の特徴です。
オプション型
抽象的表現の代表的な機能としてまず、オプション型というタイプがあります。C#にはNullable型というタイプがあります。これは、通常Null値を取ることができない値型の変数にNull値を持たすことを可能とし、値があるかないか、ある場合にはその値は何か、という情報を持つことができる型です。F#のオプション型はこのNullable型にとてもよく似ています。しかし、Nullable型は、値型にのみ有効ですが、オプション型は全てのジェネリックな型に対して有効であるという点が異なります。
//int型の変数にはそのままではnullはバインドできない。 //int x = null; ※1 //intは値型なのでNullable型に変更できる int? y = null; //※2 int? z = 1; //※3 //stringは参照型なのでNullable型にできない //string? w = "aaa";※4
※1のようにint型の変数にはnullは割り当てられません。しかし、int型はNullable型に変換できるため、※2や※3のようにして、nullもしくはint型の値を設定することは可能です。参照型の変数をNullable型にすることはできないため、※4のようにstringをNullable型に変換することはできません。
次にF#のオプション型を見てみましょう。
オプション型の構文は以下のようになります。
---------------------------------------- type ジェネリックな型 変数名 = | None | Some of 'ジェネリックな型 ----------------------------------------
では、上記の構文を用いて、先ほどC#のNullable型で定義したものをF#のオプション型を使用して表現してみましょう。
type 'a option = | None | Some of 'a;; //xにオプショナル型値なし(None)を束縛。 let x = None;; //※5 //yに値あり値は1(int)を束縛 let y = Some 1;; //※6 //yに値あり値は”aaa”(string)を束縛 let z = Some "aaa";; //※7
▼
type 'a option = | None | Some of 'a > val x : 'a option > val y : int option = Some 1 > val z : string option = Some "aaa"
上記の例の場合、Someの値はint型(※6)にもstring型(※7)にもなる可能性があります。連載第3回目にて、関数などのパラメータ化によりジェネリック(一般化)にできると解説しましたが、オプション型の型定義もパラメータにしてジェネリックにすることが可能です。Noneはnullのようなものです(※5)。実際にはこのoption型は、コンパイラ起動時からデフォルトで定義されています。
このオプション型の利用例として、リストの探索結果を返すtryFindと言う関数を紹介します。以下の例は、リストにある数値があるかどうかを判定させる関数です。
//リスト内に100という値があるかどうかの結果を返したい場合。 List.find (fun x -> x = 100) [1..10];; //tryFind関数はオプション型を返すので値がない場合にはNoneを返す。※2 List.tryFind (fun x -> x = 100) [1..10];; //tryFind関数はオプション型を返すので値がある場合には値があるということと、その値を返す。※3 List.tryFind (fun x -> x = 10) [1..10];;
▼
System.Collections.Generic.KeyNotFoundException: Exception of type 'System.Collections.Generic.KeyNotFoundException' was thrown. at Microsoft.FSharp.Collections.ListModule.Find[T](FSharpFunc`2 predicate, FSharpList`1 list) at <StartupCode$FSI_0006>.$FSI_0006.main@() Stopped due to error > val it : int option = None > val it : int option = Some 10
List.find関数はリスト内に100という値がない場合、エラーを返します(※1)。しかし、tryFind関数はオプション型を返すので値がない場合にはエラーの代わりにNoneを返します(※2)。ある場合には、Some値を表示します(※3)。