Obecné typy
Hodnoty funkcí F#, metody, vlastnosti a agregační typy, jako jsou třídy, záznamy a diskriminované sjednocení, můžou být obecné. Obecné konstruktory obsahují alespoň jeden parametr typu, který obvykle poskytuje uživatel obecného konstruktoru. Obecné funkce a typy umožňují psát kód, který funguje s různými typy bez opakování kódu pro každý typ. Vytvoření obecného kódu může být v jazyce F# jednoduché, protože často je váš kód implicitně odvozen tak, aby byl obecný pomocí odvozování typů kompilátoru a mechanismů automatické generalizace.
Syntax
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition
Poznámky
Deklarace explicitně obecné funkce nebo typu je podobně jako u negenerické funkce nebo typu, s výjimkou specifikace (a použití) parametrů typu, v úhlových závorkách za funkcí nebo názvem typu.
Deklarace jsou často implicitně obecné. Pokud plně nezadáte typ každého parametru, který se používá k vytvoření funkce nebo typu, kompilátor se pokusí odvodit typ každého parametru, hodnoty a proměnné z kódu, který píšete. Další informace najdete v tématu Odvození typu. Pokud kód pro váš typ nebo funkci jinak neomezuje typy parametrů, je funkce nebo typ implicitně obecný. Tento proces má název automatické zobecnění. Existují určitá omezení automatické generalizace. Pokud například kompilátor jazyka F# nemůže odvodit typy obecného konstruktoru, kompilátor hlásí chybu, která odkazuje na omezení označované jako omezení hodnoty. V takovém případě možná budete muset přidat nějaké poznámky k typům. Další informace o automatické generalizaci a omezení hodnoty a o tom, jak změnit kód na řešení problému, najdete v tématu Automatická generalizace.
V předchozí syntaxi jsou parametry typu seznam parametrů oddělených čárkami, které představují neznámé typy, přičemž každá z nich začíná jednou uvozovkou, volitelně s klauzulí omezení, která dále omezuje, jaké typy lze pro tento parametr typu použít. Syntaxe klauzulí omezení různých druhů a dalších informací o omezeních najdete v tématu Omezení.
Definice typu v syntaxi je stejná jako definice typu pro ne generický typ. Obsahuje parametry konstruktoru pro typ třídy, volitelnou as
klauzuli, symbol rovná se, pole záznamů, inherit
klauzuli, volby pro diskriminovanou sjednocení a do
vazby, let
definice členů a cokoli jiného povoleného v definici jiného typu.
Ostatní prvky syntaxe jsou stejné jako u negenerických funkcí a typů. Například identifikátor objektu je identifikátor, který představuje samotný objekt obsahující.
Vlastnosti, pole a konstruktory nemohou být obecnější než uzavírací typ. Hodnoty v modulu také nemohou být obecné.
Implicitně obecné konstrukce
Když kompilátor jazyka F# odvozuje typy v kódu, automaticky zachází s libovolnou funkcí, která může být obecná jako obecná. Pokud zadáte typ explicitně, například typ parametru, zabráníte automatické generalizaci.
V následujícím příkladu makeList
kódu je obecný, i když ani jeho parametry nejsou explicitně deklarovány jako obecné.
let makeList a b =
[a; b]
Podpis funkce je odvozen jako 'a -> 'a -> 'a list
. Všimněte si, že a
b
v tomto příkladu se odvozují, aby měly stejný typ. Důvodem je to, že jsou zahrnuty do seznamu společně a všechny prvky seznamu musí být stejného typu.
Funkci můžete také vytvořit obecnou pomocí syntaxe jednoduchých uvozovek v poznámkě k typu, která označuje, že typ parametru je obecný parametr typu. V následujícím kódu je obecný, function1
protože jeho parametry jsou deklarovány tímto způsobem jako parametry typu.
let function1 (x: 'a) (y: 'a) =
printfn "%A %A" x y
Explicitně obecné konstrukce
Funkci můžete také nastavit tak, že explicitně deklarujete parametry jeho typu v hranatých závorkách (<type-parameter>
). Následující kód to ilustruje.
let function2<'T> (x: 'T) (y: 'T) =
printfn "%A, %A" x y
Použití obecných konstruktorů
Pokud používáte obecné funkce nebo metody, nemusíte zadávat argumenty typu. Kompilátor používá odvozování typů k odvození příslušných argumentů typu. Pokud existuje stále nejednoznačnost, můžete zadat argumenty typu v hranatých závorkách a oddělit více argumentů typu čárkami.
Následující kód ukazuje použití funkcí definovaných v předchozích částech.
// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
// The compiler reports a warning:
function1<int> a b
// No warning.
function2<int> a b
Poznámka
Existují dva způsoby, jak odkazovat na obecný typ podle názvu. Existují například list<int>
int list
dva způsoby, jak odkazovat na obecný typ list
, který má jeden typ argumentu int
. Druhý formulář se konvenčně používá pouze s integrovanými typy F#, jako jsou a list
option
. Pokud existuje více argumentů typu, obvykle použijete syntaxi Dictionary<int, string>
, ale můžete také použít syntaxi (int, string) Dictionary
.
Zástupné důkazy jako argumenty typu
Chcete-li určit, že argument typu by měl být odvozen kompilátorem, můžete místo argumentu pojmenovaného typu použít podtržítko nebo zástupný znak (_
). Toto je znázorněno v následujícím kódu.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Omezení v obecných typech a funkcích
V obecné definici typu nebo funkce můžete použít pouze ty konstruktory, o kterých je známo, že jsou k dispozici u obecného parametru typu. To je nutné k povolení ověřování volání funkcí a metod v době kompilace. Pokud deklarujete parametry typu explicitně, můžete u obecného parametru typu použít explicitní omezení, které kompilátoru oznámí, že jsou k dispozici určité metody a funkce. Pokud ale kompilátor F# povolíte odvození obecných typů parametrů, určí pro vás příslušná omezení. Další informace najdete v tématu Omezení.
Statisticky vyřešené parametry typu
Existují dva druhy parametrů typu, které lze použít v programech F#. První jsou obecné parametry typu typu popsaného v předchozích částech. Tento první typ parametru typu je ekvivalentní obecným parametrům typu, které se používají v jazycích, jako jsou Visual Basic a C#. Další druh parametru typu je specifický pro jazyk F# a označuje se jako staticky vyřešený parametr typu. Informace o těchto konstruktorech najdete v tématu Staticky vyřešené parametry typu.
Příklady
// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
printf "%A %A" x y
// A generic record, with the type parameter in angle brackets.
type GR<'a> =
{
Field1: 'a;
Field2: 'a;
}
// A generic class.
type C<'a>(a : 'a, b : 'a) =
let z = a
let y = b
member this.GenericMethod(x : 'a) =
printfn "%A %A %A" x y z
// A generic discriminated union.
type U<'a> =
| Choice1 of 'a
| Choice2 of 'a * 'a
type Test() =
// A generic member
member this.Function1<'a>(x, y) =
printfn "%A, %A" x y
// A generic abstract method.
abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
printfn "%A, %A" x y