自動ジェネリック化Automatic Generalization

F#型の推定を使用して、関数と式の型を評価します。F# uses type inference to evaluate the types of functions and expressions. このトピックでは、どの F# 自動的に一般化引数と関数の種類可能な限り、複数の種類で動作させることについて説明します。This topic describes how F# automatically generalizes the arguments and types of functions so that they work with multiple types when this is possible.

自動ジェネリック化Automatic Generalization

コンパイラF#は、関数に対して型の推定を実行するときに、指定されたパラメーターをジェネリックにできるかどうかを判断します。The F# compiler, when it performs type inference on a function, determines whether a given parameter can be generic. コンパイラは各パラメーターを調べ、関数がそのパラメーターの特定の型に依存しているかどうかを判断します。The compiler examines each parameter and determines whether the function has a dependency on the specific type of that parameter. そうでない場合、型はジェネリックとして推論されます。If it does not, the type is inferred to be generic.

次のコード例は、コンパイラがジェネリックと推論する関数を示しています。The following code example illustrates a function that the compiler infers to be generic.

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

型はと'a -> 'a -> 'aして推論されます。The type is inferred to be 'a -> 'a -> 'a.

型は、これが同じ不明な型の2つの引数を受け取り、同じ型の値を返す関数であることを示します。The type indicates that this is a function that takes two arguments of the same unknown type and returns a value of that same type. 前の関数がジェネリックになる理由の1つは、大なり演算子 (>) 自体がジェネリックであることです。One of the reasons that the previous function can be generic is that the greater-than operator (>) is itself generic. 大なり演算子にはシグネチャ'a -> 'a -> boolがあります。The greater-than operator has the signature 'a -> 'a -> bool. すべての演算子がジェネリックであるとは限りません。関数内のコードが非ジェネリック関数または演算子と共にパラメーター型を使用している場合、そのパラメーターの型を一般化することはできません。Not all operators are generic, and if the code in a function uses a parameter type together with a non-generic function or operator, that parameter type cannot be generalized.

maxジェネリックであるため、次の例に示すようintfloat、、などの型で使用できます。Because max is generic, it can be used with types such as int, float, and so on, as shown in the following examples.

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

ただし、2つの引数は同じ型である必要があります。However, the two arguments must be of the same type. 署名はで'a -> 'a -> 'aあり、 'a -> 'b -> 'aではありません。The signature is 'a -> 'a -> 'a, not 'a -> 'b -> 'a. したがって、次のコードでは、型が一致しないため、エラーが発生します。Therefore, the following code produces an error because the types do not match.

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

関数maxは、大なり演算子をサポートするすべての型でも動作します。The max function also works with any type that supports the greater-than operator. したがって、次のコードに示すように、文字列で使用することもできます。Therefore, you could also use it on a string, as shown in the following code.

let testString = max "cab" "cat"

値の制限Value Restriction

コンパイラは、明示的な引数を持つ完全な関数定義と、単純な変更できない値に対してのみ、自動汎化を実行します。The compiler performs automatic generalization only on complete function definitions that have explicit arguments, and on simple immutable values.

これは、特定の型としては十分に制約されていないが、汎化できないコードをコンパイルしようとすると、コンパイラがエラーを発行することを意味します。This means that the compiler issues an error if you try to compile code that is not sufficiently constrained to be a specific type, but is also not generalizable. この問題のエラーメッセージは、値の制限として、値の自動汎化に対するこの制限を示しています。The error message for this problem refers to this restriction on automatic generalization for values as the value restriction.

通常、値制限エラーは、構造体をジェネリックにする必要があるものの、コンパイラがそれを一般化するための情報が不足している場合、または非ジェネリックコンストラクトで十分な型情報を誤って省略した場合に発生します。Typically, the value restriction error occurs either when you want a construct to be generic but the compiler has insufficient information to generalize it, or when you unintentionally omit sufficient type information in a nongeneric construct. 値制限エラーの解決策は、次のいずれかの方法で、型推論の問題をより詳細に制限するために、より明示的な情報を提供することです。The solution to the value restriction error is to provide more explicit information to more fully constrain the type inference problem, in one of the following ways:

  • 明示的な型の注釈を値またはパラメーターに追加することにより、型を非ジェネリックに制約します。Constrain a type to be nongeneric by adding an explicit type annotation to a value or parameter.

  • 関数コンポジションや不完全なカリー化関数の引数など、ジェネリック関数を定義するために汎化不可能なコンストラクトを使用している場合は、関数を通常の関数定義として書き直してください。If the problem is using a nongeneralizable construct to define a generic function, such as a function composition or incompletely applied curried function arguments, try to rewrite the function as an ordinary function definition.

  • 問題が複雑すぎて一般化できない式である場合は、余分な未使用のパラメーターを追加して関数にします。If the problem is an expression that is too complex to be generalized, make it into a function by adding an extra, unused parameter.

  • 明示的なジェネリック型パラメーターを追加します。Add explicit generic type parameters. このオプションはほとんど使用されません。This option is rarely used.

  • 次のコード例は、これらの各シナリオを示しています。The following code examples illustrate each of these scenarios.

ケース 1:式が複雑すぎます。Case 1: Too complex an expression. この例では、リストcounterはであることint option refを意図していますが、単純な変更できない値として定義されていません。In this example, the list counter is intended to be int option ref, but it is not defined as a simple immutable value.

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

ケース 2:汎化できない構成体を使用してジェネリック関数を定義する。Case 2: Using a nongeneralizable construct to define a generic function. この例では、構造体は、関数の引数の部分的な適用を伴うため、汎化できません。In this example, the construct is nongeneralizable because it involves partial application of function arguments.

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

ケース 3:使用されていない余分なパラメーターを追加しています。Case 3: Adding an extra, unused parameter. この式は一般化には不十分であるため、コンパイラは値の制限のエラーを発行します。Because this expression is not simple enough for generalization, the compiler issues the value restriction error.

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

ケース 4:型パラメーターを追加しています。Case 4: Adding type parameters.

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)

最後の例では、値が型の関数になり、次のように、さまざまな型の値を作成するために使用できます。In the last case, the value becomes a type function, which may be used to create values of many different types, for example as follows:

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

関連項目See also