次の方法で共有


静的に解決される型パラメーター

"静的に解決される型パラメーター" とは、実行時でなく、コンパイル時に実際の型に置き換えられる型パラメーターです。

構文

'type-parameter

F# のバージョン 7.0 までは、次の構文を使用する必要がありました

^type-parameter

解説

F# には、異なる 2 種類の型パラメーターがあります。 1 つ目の種類は、標準のジェネリック型パラメーターです。 これらは、その他の .NET 言語のジェネリック型パラメーターと同じです。 もう 1 つの種類は静的に解決され、インライン関数でのみ使用できます。

静的に解決される型パラメーターは、主にメンバー制約で使用する場合に役立ちます。これは、型引数を使用するために特定のメンバーが必要であることを指定できる制約です。 この種の制約を、標準のジェネリック型パラメーターを使用して作成する方法はありません。

2 種類の型パラメーターの類似点と相違点の概要を次の表に示します。

機能 ジェネリック 静的に解決される
解決時期 実行時 コンパイル時間
メンバー制約 メンバー制約では使用できません。 メンバー制約で使用できます。
コード生成 標準のジェネリック型パラメーターを持つ型 (またはメソッド) では、単一のジェネリック型またはジェネリック メソッドが生成されます。 各型で必要とされる、型およびメソッドの複数のインスタンス化が生成されます。
型での使用 型で使用できます。 型では使用できません。
インライン関数での使用 標準のジェネリック型パラメーターではインライン関数をパラメーター化できません。 入力が完全にジェネリックでない場合は、F# コンパイラによって特殊化されるか、特殊化するオプションがない場合はエラーが発生します。 静的に解決される型パラメーターは、インライン以外の関数またはメソッドでは使用できません。

多数の F# コア ライブラリ関数 (特に演算子) には、静的に解決される型パラメーターがあります。 これらの関数および演算子はインラインであり、数値の計算に効率的なコードが生成されます。

演算子を使用するインライン メソッドおよび関数、または静的に解決される型パラメーターを持つ他の関数を使用するインライン メソッドおよび関数では、それらのメソッドおよび関数自体でも静的に解決される型パラメーターを使用できます。 多くの場合、型推論では、それらのインライン関数が静的に解決される型パラメーターを持つと推論されます。 静的に解決される型パラメーターを持つと推論される演算子の定義を次の例に示します。

let inline (+@) x y = x + x * y
// Call that uses int.
printfn "%d" (1 +@ 1)
// Call that uses float.
printfn "%f" (1.0 +@ 0.5)

解決された (+@) の型は、(+) および (*) の使用に基づいており、これらの両方では型推論によって、静的に解決される型パラメーターによるメンバー制約が推論されます。 F# インタープリターに表示される解決された型は、次のとおりです。

'a -> 'c -> 'd
when ('a or 'b) : (static member ( + ) : 'a * 'b -> 'd) and
('a or 'c) : (static member ( * ) : 'a * 'c -> 'b)

出力は次のとおりです。

2
1.500000

次の例は、SRTP とメソッドの使用方法と静的メソッドを示しています。

type Record =
    { Number: int }
    member this.Double() = { Number = this.Number * 2 }
    static member Zero() = { Number = 0 }
    
let inline double<'a when 'a:(member Double: unit -> 'a)> (x: 'a) = x.Double()    
let inline zero<'a when 'a:(static member Zero: unit -> 'a)> () = 'a.Zero()

let r: Record = zero ()
let doubleR = double r

F# 7.0 以降では、次の例のように制約を繰り返す代わりに 'a.Zero() を使用できます。

F# 4.1 以降では、静的に解決される型パラメーター シグネチャに具象型名を指定することもできます。 以前のバージョンの言語では、型名はコンパイラで推論されましたが、シグネチャに指定することはできませんでした。 F# 4.1 以降は、静的に解決される型パラメーター シグネチャに具象型名を指定することもできます。 以下に例を示します (' を使用した単純化はサポートされていないため、この例では引き続き ^ を使用する必要があることに注意してください)。

let inline konst x _ = x

type CFunctor() =
    static member inline fmap (f: ^a -> ^b, a: ^a list) = List.map f a
    static member inline fmap (f: ^a -> ^b, a: ^a option) =
        match a with
        | None -> None
        | Some x -> Some (f x)

    // default implementation of replace
    static member inline replace< ^a, ^b, ^c, ^d, ^e when ^a :> CFunctor and (^a or ^d): (static member fmap: (^b -> ^c) * ^d -> ^e) > (a, f) =
        ((^a or ^d) : (static member fmap : (^b -> ^c) * ^d -> ^e) (konst a, f))

    // call overridden replace if present
    static member inline replace< ^a, ^b, ^c when ^b: (static member replace: ^a * ^b -> ^c)>(a: ^a, f: ^b) =
        (^b : (static member replace: ^a * ^b -> ^c) (a, f))

let inline replace_instance< ^a, ^b, ^c, ^d when (^a or ^c): (static member replace: ^b * ^c -> ^d)> (a: ^b, f: ^c) =
        ((^a or ^c): (static member replace: ^b * ^c -> ^d) (a, f))

// Note the concrete type 'CFunctor' specified in the signature
let inline replace (a: ^a) (f: ^b): ^a0 when (CFunctor or  ^b): (static member replace: ^a *  ^b ->  ^a0) =
    replace_instance<CFunctor, _, _, _> (a, f)

関連項目