Statisticky vyřešené parametry typu
Staticky vyřešený parametr typu je parametr typu, který je nahrazen skutečným typem v době kompilace místo za běhu.
Syntaxe
'type-parameter
Až do verze 7.0 jazyka F# bylo nutné použít následující syntaxi.
^type-parameter
Poznámky
V jazyce F# existují dva různé druhy parametrů typu. Prvním typem je standardní parametr obecného typu. Jsou ekvivalentní parametrům obecného typu v jiných jazycích .NET. Druhý druh je staticky vyřešený a dá se použít jenom ve vložených funkcích.
Staticky vyřešené parametry typu jsou primárně užitečné ve spojení s omezeními členů, což jsou omezení, která umožňují určit, že argument typu musí mít konkrétní člen nebo členy, aby bylo možné použít. Neexistuje způsob, jak tento druh omezení vytvořit pomocí běžného parametru obecného typu.
Následující tabulka shrnuje podobnosti a rozdíly mezi dvěma typy parametrů typu.
Funkce | Obecná | Staticky vyřešeno |
---|---|---|
Doba řešení | Operační čas | Čas kompilace |
Omezení členů | Nelze použít s omezeními členů. | Lze použít s omezeními členů. |
Generování kódu | Typ (nebo metoda) se standardními parametry obecného typu vede ke generování jednoho obecného typu nebo metody. | Vygenerují se více instancí typů a metod, jedna pro každý typ, který je potřeba. |
Použití s typy | Lze použít u typů. | Nelze použít u typů. |
Použití s vloženými funkcemi | Vloženou funkci nelze parametrizovat pomocí standardního parametru obecného typu. Pokud vstupy nejsou plně obecné, kompilátor jazyka F# je specializuje nebo pokud neexistují žádné možnosti pro specializaci, zobrazí chybu. | Staticky vyřešené parametry typu nelze použít u funkcí nebo metod, které nejsou vložené. |
Mnoho funkcí základní knihovny jazyka F#, zejména operátorů, má staticky vyřešené parametry typu. Tyto funkce a operátory jsou vložené a výsledkem je efektivní generování kódu pro číselné výpočty.
Vložené metody a funkce, které používají operátory, nebo používají jiné funkce, které mají staticky vyřešené parametry typu, mohou také použít staticky vyřešené parametry typu samy. Odvození typu často odvodí takové vložené funkce tak, aby měly staticky vyřešené parametry typu. Následující příklad znázorňuje definici operátoru, která je odvozena tak, aby měla staticky vyřešený parametr typu.
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)
Vyřešený typ (+@)
je založen na použití obou (+)
a (*)
, z nichž oba způsobují odvození typu omezení členů na staticky vyřešené parametry typu. Vyřešený typ, jak je znázorněno v interpretu jazyka F#, je následující.
'a -> 'c -> 'd
when ('a or 'b) : (static member ( + ) : 'a * 'b -> 'd) and
('a or 'c) : (static member ( * ) : 'a * 'c -> 'b)
Výstup je následující.
2
1.500000
Následující příklad ukazuje použití SRTPs s metodami a statickými metodami:
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
Počínaje jazykem F# 7.0 můžete místo opakování omezení použít 'a.Zero()
jako v následujícím příkladu.
Počínaje jazykem F# 4.1 můžete také zadat konkrétní názvy typů v staticky přeložených podpisech parametrů typu. V předchozích verzích jazyka byl název typu odvozen kompilátorem, ale nelze ho zadat v podpisu. Od verze F# 4.1 můžete také zadat konkrétní názvy typů v staticky vyřešených podpisech parametrů typu. Tady je příklad (mějte na paměti, že v tomto příkladu je nutné použít i nadále, ^
protože zjednodušení pro použití '
není podporováno):
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)