次の方法で共有


自動一般化

F# では、型推論を使用して関数と式の型を評価します。 このトピックでは、可能な場合に複数の型を操作できるように、F# が関数の引数と型を自動的に一般化する方法について説明します。

自動一般化

F# コンパイラは、関数に対して型推論を実行するときに、指定されたパラメーターをジェネリックにできるかどうかを判断します。 コンパイラは各パラメーターを調べ、関数がそのパラメーターの特定の型に依存しているかどうかを判断します。 そうでない場合、型はジェネリックであると推論されます。

次のコード例は、コンパイラがジェネリックであると推論する関数を示しています。

let max a b = if a > b then a else b

型は 'a -> 'a -> 'aと推定されます。

この型は、これが同じ不明な型の 2 つの引数を受け取り、その同じ型の値を返す関数であることを示します。 前の関数がジェネリックになる理由の 1 つは、より大きい演算子 (>) 自体がジェネリックであるということです。 より大きい演算子には、署名 'a -> 'a -> boolがあります。 すべての演算子がジェネリックであるわけではありません。また、関数内のコードが非ジェネリック関数または演算子と共にパラメーター型を使用する場合、そのパラメーター型を一般化することはできません。

maxはジェネリックであるため、次の例に示すように、intfloatなどの型で使用できます。

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

ただし、2 つの引数は同じ型である必要があります。 署名は'a -> 'b -> 'aではなく'a -> 'a -> 'a。 したがって、次のコードでは、型が一致しないため、エラーが発生します。

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

max関数は、より大きい演算子をサポートする任意の型でも動作します。 そのため、次のコードに示すように、文字列で使用することもできます。

let testString = max "cab" "cat"

値の制限

コンパイラは、明示的な引数を持つ完全な関数定義と、単純な変更できない値に対してのみ、自動一般化を実行します。

これは、特定の型に十分に制約されていないが、一般化できないコードをコンパイルしようとすると、コンパイラがエラーを発行することを意味します。 この問題のエラー メッセージは、値の自動一般化に関するこの制限を 値の制限として参照します。

通常、値制限エラーは、コンストラクトをジェネリックにしたいが、コンパイラに一般化するための情報が不足している場合、または非ジェネリック コンストラクトで十分な型情報を意図せずに省略した場合に発生します。 値制限エラーの解決策は、次のいずれかの方法で、型推論の問題をより完全に制約するために、より明示的な情報を提供することです。

  • 値またはパラメーターに明示的な型注釈を追加して、型を非ジェネリックに制限します。

  • 問題が非ジェネリックコンストラクトを使用して関数コンポジションや不完全に適用されたカリー関数引数などのジェネリック関数を定義している場合は、関数を通常の関数定義として書き換えてみてください。

  • 問題が複雑すぎて一般化できない式の場合は、未使用のパラメーターを追加して関数にします。

  • 明示的なジェネリック型パラメーターを追加します。 このオプションはほとんど使用しません。

次のコード例は、これらの各シナリオを示しています。

ケース 1: 式が複雑すぎます。 この例では、 counter リストは int option refを目的としていますが、単純な変更できない値として定義されていません。

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

ケース 2: ジェネリック関数を定義するために非ジェネリック化可能なコンストラクトを使用する。 この例では、関数引数の部分的な適用が伴うため、コンストラクトは汎用化できません。

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

ケース 3: 未使用の追加パラメーターを追加する。 この式は一般化には十分に単純ではないため、コンパイラは値制限エラーを発行します。

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

ケース 4: 型パラメーターの追加。

let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)

最後のケースでは、値は型関数になります。これは、次のように、さまざまな型の値を作成するために使用できます。

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

こちらも参照ください