Dela via


Statiskt lösta typparametrar

En statiskt matchad typparameter är en typparameter som ersätts med en faktisk typ vid kompileringstillfället i stället för vid körning.

Syntax

'type-parameter

Upp till version 7.0 av F#, var man tvungen att använda följande syntax

^type-parameter

Kommentarer

I F# finns det två olika typer av typparametrar. Den första typen är standardparametern för generisk typ. De motsvarar generiska typparametrar på andra .NET-språk. Den andra typen löses statiskt och kan endast användas i inlinade funktioner.

Statiskt lösta typparametrar är främst användbara tillsammans med medlemsbegränsningar, som är begränsningar som gör att du kan ange att ett typargument måste ha en viss medlem eller medlemmar för att kunna användas. Det finns inget sätt att skapa den här typen av villkor med hjälp av en vanlig allmän typparameter.

I följande tabell sammanfattas likheterna och skillnaderna mellan de två typerna av typparametrar.

Funktion Allmän Statiskt löst
Lösningstid Körningstid Kompileringstid
Medlemsbegränsningar Det går inte att använda med medlemsbegränsningar. Kan användas med medlemsbegränsningar.
Kodgenerering En typ (eller metod) med standardparametrar av allmän typ resulterar i generering av en enda generisk typ eller metod. Flera instansieringar av typer och metoder genereras, en för varje typ som behövs.
Använda med typer Kan användas på typer. Det går inte att använda på typer.
Använda med infogade funktioner En infogad funktion kan inte parametriseras med en standardparameter av allmän typ. Om indata inte är helt generiska, specialiserar F#-kompilatorn dem eller, om det inte finns några alternativ att specialisera sig på, ger ett fel. Statiskt lösta typparametrar kan inte användas på funktioner eller metoder som inte är infogade.

Många F#-kärnbiblioteksfunktioner, särskilt operatorer, har statiskt lösta typparametrar. Dessa funktioner och operatorer är infogade och resulterar i effektiv kodgenerering för numeriska beräkningar.

Infogade metoder och funktioner som använder operatorer, eller använder andra funktioner som har statiskt lösta typparametrar, kan också använda statiskt lösta typparametrar själva. Ofta kan typinferens dra slutsatsen att sådana infogade funktioner har statiskt lösta typparametrar. I följande exempel visas en operatordefinition som härleds till att ha en statiskt löst typparameter.

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)

Den lösta typen av (+@) baseras på användningen av båda (+) och (*), som båda orsakar typinferens för att härleda medlemsbegränsningar för de statiskt lösta typparametrarna. Den lösta typen, som visas i F#-tolken, är följande.

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

Utdata är följande.

2
1.500000

I följande exempel visas användningen av SRTPs med metoder och statiska metoder:

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

Från och med F# 7.0 kan du använda 'a.Zero() i stället för att behöva upprepa villkoret som i exemplet nedan.

Från och med F# 4.1 kan du också ange konkreta typnamn i statiskt lösta typparametersignaturer. I tidigare versioner av språket härleddes typnamnet av kompilatorn, men det gick inte att ange i signaturen. Från och med F# 4.1 kan du även ange konkreta typnamn i statiskt lösta typparametersignaturer. Här är ett exempel (observera att i det här exemplet ^ måste fortfarande användas eftersom förenklingen att använda ' inte stöds):

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)

Se även