自動ジェネリック化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. この例で、リスト counterint 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