Automatikus általánosítás
Az F# típuskövetkeztetést használ a függvények és kifejezések típusainak kiértékeléséhez. Ez a témakör azt ismerteti, hogy az F# hogyan általánosítja automatikusan az argumentumokat és a függvénytípusokat, hogy több típussal is működhessenek, ha ez lehetséges.
Automatikus általánosítás
Az F#-fordító, amikor típuskövetkeztetést végez egy függvényen, meghatározza, hogy egy adott paraméter általános lehet-e. A fordító megvizsgálja az egyes paramétereket, és meghatározza, hogy a függvény függ-e a paraméter adott típusától. Ha nem, a típus általánosnak lesz kikövetkeztetve.
Az alábbi példakód egy olyan függvényt mutat be, amelyből a fordító általánosan következtet.
let max a b = if a > b then a else b
A típus a következőre lesz következtetve 'a -> 'a -> 'a
: .
A típus azt jelzi, hogy ez egy olyan függvény, amely két azonos típusú argumentumot vesz fel, és egy azonos típusú értéket ad vissza. Az egyik oka annak, hogy az előző függvény általános lehet, az, hogy a nagyobb operátor (>
) maga is általános. A nagyobb, mint operátor rendelkezik az aláírás 'a -> 'a -> bool
. Nem minden operátor általános, és ha egy függvény kódja paramétertípust használ egy nem általános függvénnyel vagy operátorral együtt, akkor ez a paramétertípus nem általánosítható.
Mivel max
általános, olyan típusokkal is használható, mint int
például a , float
és így tovább, ahogy az alábbi példákban is látható.
let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3
A két argumentumnak azonban azonos típusúnak kell lennie. Az aláírás nem 'a -> 'a -> 'a
'a -> 'b -> 'a
. Ezért a következő kód hibát eredményez, mert a típusok nem egyeznek.
// Error: type mismatch.
let biggestIntFloat = max 2.0 3
A max
függvény minden olyan típussal is működik, amely támogatja a nagyobb operátort. Ezért használhatja sztringen is, ahogy az az alábbi kódban is látható.
let testString = max "cab" "cat"
Értékkorlátozás
A fordító csak explicit argumentumokkal rendelkező teljes függvénydefiníciókon és egyszerű nem módosítható értékeken hajtja végre az automatikus általánosítást.
Ez azt jelenti, hogy a fordító hibát ad ki, ha olyan kódot próbál lefordítani, amely nem elég korlátozott egy adott típushoz, de nem általánosítható. A probléma hibaüzenete az értékek automatikus általánosítására vonatkozó korlátozásra hivatkozik értékkorlátozásként.
Az értékkorlátozási hiba általában akkor fordul elő, ha egy szerkezetet általánossá szeretne tenni, de a fordító nem rendelkezik elegendő információval az általánosításhoz, vagy ha véletlenül kihagy elegendő típusinformációt egy nemgenerikus szerkezetben. Az értékkorlátozási hiba megoldása az, hogy explicitebb információkat nyújt a típuskövetkeztetési probléma teljes korlátozásához az alábbi módszerek egyikével:
A típusok nemgenerikusnak való korlátozásához adjon hozzá explicit típusú széljegyzetet egy értékhez vagy paraméterhez.
Ha a probléma egy nemgeneralizálható szerkezettel határoz meg egy általános függvényt, például egy függvényösszetételt vagy a nem megfelelően alkalmazott, curried függvényargumentumokat, próbálja meg újraírni a függvényt szokásos függvénydefinícióként.
Ha a probléma egy olyan kifejezés, amely túl összetett ahhoz, hogy általánosíthassa, egy további, nem használt paraméter hozzáadásával alakítsa azt egy függvénybe.
Adjon hozzá explicit általános típusparamétereket. Ezt a beállítást ritkán használják.
Az alábbi példakódok ezeket a forgatókönyveket szemléltetik.
1. eset: Túl összetett kifejezés. Ebben a példában a lista counter
célja az, int option ref
hogy legyen, de nem egyszerű megváltoztathatatlan értékként van definiálva.
let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None
2. eset: Általános függvény definiálása nemgenerálható szerkezettel. Ebben a példában a szerkezet nemgeneralizálható, mert függvényargumentumok részleges alkalmazását foglalja magában.
let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj
3. eset: További, nem használt paraméter hozzáadása. Mivel ez a kifejezés nem elég egyszerű az általánosításhoz, a fordító az értékkorlátozási hibát adja ki.
let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []
4. eset: Típusparaméterek hozzáadása.
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)
Az utolsó esetben az érték típusfüggvény lesz, amely számos különböző típusú érték létrehozására használható, például az alábbiak szerint:
let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>