F# 関数の値、メソッド、プロパティ、およびクラス、レコード、判別共用体などの集計型を ジェネリックにすることができます。 ジェネリック コンストラクトには、少なくとも 1 つの型パラメーターが含まれています。これは通常、ジェネリック コンストラクトのユーザーによって提供されます。 ジェネリック関数と型を使用すると、型ごとにコードを繰り返すことなく、さまざまな型で動作するコードを記述できます。 多くの場合、コードはコンパイラの型推論と自動一般化メカニズムによってジェネリックであると暗黙的に推論されるため、F# ではコードをジェネリックにすることは簡単です。
構文
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition
注釈
明示的にジェネリック関数または型の宣言は、関数または型名の後の山かっこで囲まれた型パラメーターの指定 (および使用) を除き、非ジェネリック関数または型とよく似ています。
多くの場合、宣言は暗黙的にジェネリックです。 関数または型の作成に使用されるすべてのパラメーターの型を完全に指定しない場合、コンパイラは、記述したコードから各パラメーター、値、変数の型を推論しようとします。 詳細については、「 型推論」を参照してください。 型または関数のコードがパラメーターの型を制約しない場合、関数または型は暗黙的にジェネリックになります。 このプロセスの名前は 自動一般化です。 自動一般化にはいくつかの制限があります。 たとえば、F# コンパイラがジェネリック コンストラクトの型を推論できない場合、コンパイラは 値制限と呼ばれる制限を参照するエラーを報告します。 その場合は、いくつかの型注釈を追加する必要があります。 自動一般化と値の制限の詳細と、問題に対処するためにコードを変更する方法については、「 自動一般化」を参照してください。
前の構文では、 型パラメーター は不明な型を表すパラメーターのコンマ区切りのリストです。各パラメーターは単一引用符で始まり、必要に応じて、その型パラメーターに使用できる型をさらに制限する制約句を使用します。 さまざまな種類の制約句の構文と制約に関するその他の情報については、「 制約」を参照してください。
構文の 型定義 は、非ジェネリック型の型定義と同じです。 これには、クラス型のコンストラクター パラメーター、省略可能な as
句、等しいシンボル、レコード フィールド、 inherit
句、判別共用体の選択肢、 let
バインドと do
バインド、メンバー定義、および非ジェネリック型定義で許可されているその他のものが含まれます。
他の構文要素は、非ジェネリック関数および型の構文要素と同じです。 たとえば、 オブジェクト識別子 は、含まれているオブジェクト自体を表す識別子です。
プロパティ、フィールド、およびコンストラクターは、外側の型よりもジェネリックにすることはできません。 また、モジュール内の値をジェネリックにすることはできません。
暗黙的にジェネリックコンストラクト
F# コンパイラは、コード内の型を推論すると、ジェネリックにすることができるすべての関数を自動的にジェネリックとして扱います。 パラメーター型など、型を明示的に指定すると、自動一般化は行われません。
次のコード例では、 makeList
はジェネリックですが、パラメーターも明示的にジェネリックとして宣言されていません。
let makeList a b = [ a; b ]
関数のシグネチャは、 'a -> 'a -> 'a list
されると推論されます。 この例の a
と b
は、同じ型であると推論されることに注意してください。 これは、リストに一緒に含まれており、リストのすべての要素が同じ型である必要があるためです。
パラメーター型がジェネリック型パラメーターであることを示すために、型注釈で単一引用符構文を使用して関数をジェネリックにすることもできます。 次のコードでは、 function1
パラメーターは型パラメーターとしてこの方法で宣言されているため、ジェネリックです。
let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y
明示的にジェネリックコンストラクト
山かっこ (<type-parameter>
) で型パラメーターを明示的に宣言することで、関数をジェネリックにすることもできます。 次のコードは、これを示しています。
let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y
ジェネリック コンストラクトの使用
ジェネリック関数またはメソッドを使用する場合は、型引数を指定する必要がない場合があります。 コンパイラは、型推論を使用して、適切な型引数を推論します。 あいまいさが引き続き存在する場合は、山かっこで囲まれた型引数を指定し、複数の型引数をコンマで区切ることができます。
次のコードは、前のセクションで定義した関数の使用方法を示しています。
// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
// The compiler reports a warning:
function1<int> a b
// No warning.
function2<int> a b
注
名前でジェネリック型を参照するには、2 つの方法があります。 たとえば、list<int>
とint list
は、単一の型引数int
を持つジェネリック型list
を参照する 2 つの方法です。 後者の形式は、通常、 list
や option
などの組み込みの F# 型でのみ使用されます。 複数の型引数がある場合は、通常、構文 Dictionary<int, string>
を使用しますが、構文 (int, string) Dictionary
を使用することもできます。
型引数としてのワイルドカード
コンパイラによって型引数を推論するように指定するには、名前付き型引数の代わりにアンダースコアまたはワイルドカード記号 (_
) を使用できます。 これは次のコードに示されています。
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
ジェネリック型と関数の制約
ジェネリック型または関数定義では、ジェネリック型パラメーターで使用できることがわかっているコンストラクトのみを使用できます。 これは、コンパイル時に関数とメソッド呼び出しの検証を有効にするために必要です。 型パラメーターを明示的に宣言する場合は、ジェネリック型パラメーターに明示的な制約を適用して、特定のメソッドと関数が使用可能であることをコンパイラに通知できます。 ただし、F# コンパイラにジェネリック パラメーター型の推論を許可すると、適切な制約が決定されます。 詳細については、「制約」を参照してください。
静的に解決された型パラメーター
F# プログラムでは、2 種類の型パラメーターを使用できます。 1 つ目は、前のセクションで説明した種類のジェネリック型パラメーターです。 この最初の種類の型パラメーターは、Visual Basic や C# などの言語で使用されるジェネリック型パラメーターと同じです。 別の種類の型パラメーターは F# に固有であり、 静的に解決された型パラメーターと呼ばれます。 これらのコンストラクトの詳細については、「 静的に解決される型パラメーター」を参照してください。
例示
// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x: 'a) (y: 'a) = printf "%A %A" x y
// A generic record, with the type parameter in angle brackets.
type GR<'a> = { Field1: 'a; Field2: 'a }
// A generic class.
type C<'a>(a: 'a, b: 'a) =
let z = a
let y = b
member this.GenericMethod(x: 'a) = printfn "%A %A %A" x y z
// A generic discriminated union.
type U<'a> =
| Choice1 of 'a
| Choice2 of 'a * 'a
type Test() =
// A generic member
member this.Function1<'a>(x, y) = printfn "%A, %A" x y
// A generic abstract method.
abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
override this.abstractMethod<'a, 'b>(x: 'a, y: 'b) = printfn "%A, %A" x y
こちらも参照ください
.NET