Automatická generalizace

Jazyk F# používá odvození typu k vyhodnocení typů funkcí a výrazů. Toto téma popisuje, jak jazyk F# automaticky zobecní argumenty a typy funkcí, aby fungovaly s více typy, pokud je to možné.

Automatická generalizace

Kompilátor jazyka F# při provádění odvození typu u funkce určuje, zda daný parametr může být obecný. Kompilátor zkoumá jednotlivé parametry a určuje, jestli má funkce závislost na konkrétním typu tohoto parametru. Pokud tomu tak není, typ je odvozen jako obecný.

Následující příklad kódu znázorňuje funkci, kterou kompilátor odvodí jako obecný.

let max a b = if a > b then a else b

Typ je odvozen jako 'a -> 'a -> 'a.

Typ označuje, že se jedná o funkci, která přebírá dva argumenty stejného neznámého typu a vrací hodnotu stejného typu. Jedním z důvodů, proč může být předchozí funkce obecná, je, že operátor větší než (>) je obecný. Operátor větší než má podpis 'a -> 'a -> bool. Ne všechny operátory jsou obecné a pokud kód funkce používá typ parametru společně s ne generickou funkcí nebo operátorem, nelze tento typ parametru generalizovat.

Vzhledem k tomu max , že je obecný, lze jej použít s typy, jako intje , floata tak dále, jak je znázorněno v následujících příkladech.

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

Oba argumenty však musí být stejného typu. Podpis je 'a -> 'a -> 'a, ne 'a -> 'b -> 'a. Proto následující kód vytvoří chybu, protože typy se neshodují.

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

Funkce max také funguje s libovolným typem, který podporuje operátor větší než. Proto byste ho mohli použít také v řetězci, jak je znázorněno v následujícím kódu.

let testString = max "cab" "cat"

Omezení hodnoty

Kompilátor provádí automatickou generalizaci pouze u úplných definic funkcí, které mají explicitní argumenty, a na jednoduchých neměnných hodnotách.

To znamená, že kompilátor vydá chybu, pokud se pokusíte zkompilovat kód, který není dostatečně omezený na konkrétní typ, ale není také generalizovatelný. Chybová zpráva pro tento problém odkazuje na toto omezení automatické zobecnění pro hodnoty jako omezení hodnoty.

K chybě omezení hodnoty obvykle dochází buď v případě, že chcete, aby byl konstruktor obecný, ale kompilátor nemá dostatek informací ke generalizaci, nebo když neúmyslně vynecháte dostatečné informace o typu v negenerické konstruktoru. Řešením chyby omezení hodnoty je poskytnout explicitnější informace pro větší omezení problému odvozování typu jedním z následujících způsobů:

  • Omezte typ tak, aby byl negenerický přidáním explicitní poznámky k typu k hodnotě nebo parametru.

  • Pokud problém používá negeneralizovatelný konstruktor k definování obecné funkce, jako je například složení funkce nebo neúplné použití složených argumentů funkce, zkuste funkci přepsat jako běžnou definici funkce.

  • Pokud je problém výrazem, který je příliš složitý na generalizaci, vytvořte ho do funkce přidáním nadbytečného nepoužitého parametru.

  • Přidejte explicitní parametry obecného typu. Tato možnost se používá zřídka.

Následující příklady kódu ilustrují každý z těchto scénářů.

Případ 1: Příliš složitý výraz. V tomto příkladu má být int option refseznam counter , ale není definován jako jednoduchá neměnná hodnota.

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

Případ 2: Použití negeneralizovatelné konstrukce k definování obecné funkce. V tomto příkladu je konstruktor negeneralizovatelný, protože zahrnuje částečnou aplikaci argumentů funkce.

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

Případ 3: Přidání nadbytečného nepoužitého parametru Vzhledem k tomu, že tento výraz není dostatečně jednoduchý pro generalizaci, kompilátor vydá chybu omezení hodnoty.

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

Případ 4: Přidání parametrů typu

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)

V posledním případě se hodnota stane funkcí typu, která se může použít k vytvoření hodnot mnoha různých typů, například takto:

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

Viz také