サーチ…
オプションの定義
Optionは、 NoneまたはSome 2つのケースで区別されたユニオンです。
type Option<'T> = Some of 'T | None
NULL値に対してOption <'T>を使用する
F#ような関数型プログラミング言語では、値は潜在的に有害で貧弱なスタイル(非慣用的)とみなされnull 。
このC#コードを考えてみましょう:
string x = SomeFunction ();
int l = x.Length;
xがnull場合はx.Lengthがスローされnull保護を追加しましょう:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
または:
string x = SomeFunction () ?? "";
int l = x.Length;
または:
string x = SomeFunction ();
int l = x?.Length;
慣用句F# null値は使用されないので、コードは次のようになります。
let x = SomeFunction ()
let l = x.Length
ただし、空の値または無効な値を表す必要があることがあります。それから、 Option<'T>使うことができます:
let SomeFunction () : string option = ...
SomeFunctionは、 Some string値またはNone返します。パターンマッチングを用いてstring値を抽出する
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
このコードが以下の理由より脆弱でない理由は:
string x = SomeFunction ();
int l = x.Length;
string option Lengthを呼び出すことができないからです。パターンマッチングを使用してstring値を抽出する必要があります。そうすることで、 string値が安全に使用できることが保証されます。
オプションモジュールにより鉄道指向プログラミングが可能
エラー処理は重要ですが、優雅なアルゴリズムを混乱させる可能性があります。 鉄道指向プログラミング ( ROP )は、エラー処理をエレガントで構成可能にするために使用されます。
簡単な関数f考えてみましょう。
let tryParse s =
let b, v = System.Int32.TryParse s
if b then Some v else None
let f (g : string option) : float option =
match g with
| None -> None
| Some s ->
match tryParse s with // Parses string to int
| None -> None
| Some v when v < 0 -> None // Checks that int is greater than 0
| Some v -> v |> float |> Some // Maps int to float
目的f 、入力解析するstring (存在する場合は値をSome )にint 。 intが0より大きい場合は、 floatキャストします。それ以外の場合は、「 None 」で救済する。
ただし、ネストされたmatchが非常に単純な関数では、可読性が大幅に低下します。
ROPはプログラムで2種類の実行パスがあることを確認しています
- ハッピーパス-最終的に計算します
Some値を - エラーパス-他のすべてのパスは発生しない
None
エラー・パスは頻繁に発生するため、コードを引き継ぐ傾向があります。ハッピーなパスコードが最も目に見えるコードパスであることを望みます。
ROPを使った同等の関数g 、次のようになります。
let g (v : string option) : float option =
v
|> Option.bind tryParse // Parses string to int
|> Option.filter ((<) 0) // Checks that int is greater than 0
|> Option.map float // Maps int to float
F#でリストやシーケンスを処理する傾向によく似ています。
一つは、見ることができるOption<'T>のようなList<'T>のみ含まれていてもよい0または1の要素Option.bind同じように動作しList.pick (概念的Option.bindより良いにマップをList.collectが、 List.pickあるかもしれません理解しやすい)。
bind 、 filter 、およびmapはエラー・パスを処理し、 gはハッピー・パス・コードのみを処理します。
直接受け入れるすべての機能Option<_>と返すOption<_>との直接構成可能です|>と>> 。
したがってROPは可読性と合成性を向上させます。
C#のオプションタイプを使用する
Option型をC#コードに公開するのは良い考えではありません.C#にはそれらを処理する方法がないからです。オプションは、C#プロジェクトの依存関係としてFSharp.Coreを導入するか(C#との相互運用用に設計されていない F#ライブラリを使用している場合に必要な作業です)、またはNone値をnullに変更しnull 。
Pre-F#4.0
これを行う方法は、あなた自身の変換関数を作成することです:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
値の型は、ボクシングやSystem.Nullableを使用する必要があります。
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F#4.0
F#4.0では、 Optionモジュールに導入されたofObj 、 toObj 、 ofNullable 、およびtoNullableが使用されています。 F#インタラクティブでは、次のように使用できます。
let l1 = [ Some 1 ; None ; Some 2]
let l2 = l1 |> List.map Option.toNullable;;
// val l1 : int option list = [Some 1; null; Some 2]
// val l2 : System.Nullable<int> list = [1; null; 2]
let l3 = l2 |> List.map Option.ofNullable;;
// val l3 : int option list = [Some 1; null; Some 2]
// Equality
l1 = l2 // fails to compile: different types
l1 = l3 // true
None内部的にnullコンパイルされることに注意してください。しかし、F#に関する限り、それはNoneです。
let lA = [Some "a"; None; Some "b"]
let lB = lA |> List.map Option.toObj
// val lA : string option list = [Some "a"; null; Some "b"]
// val lB : string list = ["a"; null; "b"]
let lC = lB |> List.map Option.ofObj
// val lC : string option list = [Some "a"; null; Some "b"]
// Equality
lA = lB // fails to compile: different types
lA = lC // true