Automatische generalisatie

F# gebruikt typedeductie om de typen functies en expressies te evalueren. In dit onderwerp wordt beschreven hoe F# de argumenten en typen functies automatisch generaliseert, zodat deze werken met meerdere typen wanneer dit mogelijk is.

Automatische generalisatie

Wanneer de F#-compiler typedeductie uitvoert voor een functie, wordt bepaald of een bepaalde parameter algemeen kan zijn. De compiler onderzoekt elke parameter en bepaalt of de functie afhankelijk is van het specifieke type van die parameter. Als dit niet het geval is, wordt het type afgeleid als algemeen.

In het volgende codevoorbeeld ziet u een functie die door de compiler als algemeen wordt afgeleid.

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

Het type wordt afgeleid.'a -> 'a -> 'a

Het type geeft aan dat dit een functie is die twee argumenten van hetzelfde onbekende type gebruikt en een waarde van hetzelfde type retourneert. Een van de redenen waarom de vorige functie algemeen kan zijn, is dat de operator groter dan (>) zelf algemeen is. De operator groter dan heeft de handtekening 'a -> 'a -> bool. Niet alle operators zijn algemeen en als de code in een functie een parametertype samen met een niet-algemene functie of operator gebruikt, kan dat parametertype niet worden gegeneraliseerd.

Omdat max het algemeen is, kan het worden gebruikt met typen, zoals int, floatenzovoort, zoals wordt weergegeven in de volgende voorbeelden.

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

De twee argumenten moeten echter van hetzelfde type zijn. De handtekening is 'a -> 'a -> 'a, niet 'a -> 'b -> 'a. Daarom produceert de volgende code een fout omdat de typen niet overeenkomen.

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

De max functie werkt ook met elk type dat de operator groter dan ondersteunt. Daarom kunt u deze ook gebruiken voor een tekenreeks, zoals wordt weergegeven in de volgende code.

let testString = max "cab" "cat"

Waardebeperking

De compiler voert automatische generalisatie alleen uit op volledige functiedefinities met expliciete argumenten en op eenvoudige onveranderbare waarden.

Dit betekent dat de compiler een fout optreedt als u probeert code te compileren die niet voldoende beperkt is om een specifiek type te zijn, maar ook niet generaliseerbaar is. Het foutbericht voor dit probleem verwijst naar deze beperking voor automatische generalisatie voor waarden als waardebeperking.

Normaal gesproken treedt de fout met waardebeperking op wanneer u wilt dat een constructie algemeen is, maar de compiler onvoldoende informatie heeft om deze te generaliseren of wanneer u onbedoeld voldoende typegegevens weglaat in een niet-generische constructie. De oplossing voor de fout met waardebeperking is om meer expliciete informatie te verstrekken om het probleem met typedeductie volledig te beperken, op een van de volgende manieren:

  • Beperk een type dat niet-gegenereerd moet zijn door een expliciete typeaantekening toe te voegen aan een waarde of parameter.

  • Als het probleem gebruikmaakt van een niet-generaliseerbare constructie om een algemene functie te definiĆ«ren, zoals een functiesamenstelling of onvolledig toegepaste argumenten voor een functie, probeert u de functie opnieuw te herschrijven als een gewone functiedefinitie.

  • Als het probleem een expressie is die te complex is om te worden gegeneraliseerd, maakt u deze in een functie door een extra, ongebruikte parameter toe te voegen.

  • Voeg expliciete algemene typeparameters toe. Deze optie wordt zelden gebruikt.

De volgende codevoorbeelden illustreren elk van deze scenario's.

Case 1: Een expressie is te complex. In dit voorbeeld is de lijst counter bedoeld om te zijn int option ref, maar deze is niet gedefinieerd als een eenvoudige onveranderbare waarde.

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

Case 2: Een niet-generalizable constructie gebruiken om een algemene functie te definiƫren. In dit voorbeeld is de constructie niet-generaliseerbaar omdat deze gedeeltelijke toepassing van functieargumenten omvat.

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

Case 3: Een extra, ongebruikte parameter toevoegen. Omdat deze expressie niet eenvoudig genoeg is voor generalisatie, geeft de compiler de fout met betrekking tot waardebeperking uit.

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

Case 4: Typeparameters toevoegen.

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)

In het laatste geval wordt de waarde een typefunctie, die kan worden gebruikt om waarden van veel verschillende typen te maken, bijvoorbeeld als volgt:

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

Zie ook