柔軟な型 (F#)
柔軟な型の注釈は、パラメーター、変数、または値の型が、指定された型と互換性があることを示します。互換性は、クラスまたはインターフェイスのオブジェクト指向の階層における位置によって決まります。 柔軟な型は、型階層における上位の型への自動変換が発生しない場合でも、階層内の任意の型またはインターフェイスを実装する任意の型を操作できるようにする場合に特に便利です。
#type
解説
前の構文で、type は基本型またはインターフェイスを表します。
柔軟な型は、許可される型を基本型またはインターフェイス型と互換性がある型に限定する制約があるジェネリック型と等価です。 つまり、次の 2 つのコード行は等価です。
#SomeType
'a when 'a :> SomeType
柔軟な型は、いくつかの種類の状況で便利です。 たとえば、高階関数 (関数を引数として受け取る関数) がある場合、その関数で柔軟な型を返すと便利です。 次の例では、柔軟な型をシーケンス引数と共に iterate2 で使用することにより、高階関数で、シーケンス、配列、リスト、および他の列挙可能な型を生成する関数を使用できるようになります。
次のような 2 つの関数があるとします。1 つはシーケンスを返し、もう 1 つは柔軟な型を返します。
let iterate1 (f : unit -> seq<int>) =
for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
for e in f() do printfn "%d" e
// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)
// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
別の例として、Seq.concat ライブラリ関数を示します。
val concat: sequences:seq<#seq<'T>> -> seq<'T>
次の任意の列挙可能なシーケンスを、この関数に渡すことができます。
リストのリスト
配列のリスト
リストの配列
シーケンスの配列
列挙可能なシーケンスの他の組み合わせ
次のコードでは、Seq.concat を使用して、柔軟な型の使用によりサポートできるシナリオを示しています。
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]
let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1
let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]
let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2
let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3
let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4
let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }
let concat5 = Seq.concat [| seq1; seq2; seq3 |]
printfn "%A" concat5
出力は次のとおりです。
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
F# では、他のオブジェクト指向言語と同じく、派生型またはインターフェイスを実装する型が基本型またはインターフェイス型に自動的に変換されるコンテキストが存在します。 これらの自動変換は直接引数で発生しますが、関数型の戻り値の型など、より複雑な型の一部として、または型引数として、型が下位の位置にあるときは発生しません。 したがって、柔軟な型の表記は、この表記を適用する型が、より複雑な型の一部である場合に特に便利です。