Flexibla typer
En flexibel typanteckning anger att en parameter, variabel eller ett värde har en typ som är kompatibel med en angiven typ, där kompatibilitet bestäms av position i en objektorienterad hierarki med klasser eller gränssnitt. Flexibla typer är särskilt användbara när den automatiska konverteringen till typer som är högre i typhierarkin inte sker, men du fortfarande vill att dina funktioner ska fungera med någon typ i hierarkin eller någon typ som implementerar ett gränssnitt.
Syntax
#type
Kommentarer
I föregående syntax representerar typen en bastyp eller ett gränssnitt.
En flexibel typ motsvarar en allmän typ som har en begränsning som begränsar de tillåtna typerna till typer som är kompatibla med bas- eller gränssnittstypen. Det vill: följande två kodrader är likvärdiga.
#SomeType
'T when 'T :> SomeType
Flexibla typer är användbara i flera typer av situationer. När du till exempel har en funktion med högre ordning (en funktion som tar en funktion som argument) är det ofta användbart att funktionen returnerar en flexibel typ. I följande exempel gör användningen av en flexibel typ med ett sekvensargument i iterate2
att funktionen med högre ordning kan fungera med funktioner som genererar sekvenser, matriser, listor och andra uppräkningsbara typer.
Tänk på följande två funktioner, varav en returnerar en sekvens, varav den andra returnerar en flexibel typ.
let iterate1 (f : unit -> seq<int>) =
for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
for e in f() do printfn "%d" e
// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)
// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
Som ett annat exempel bör du överväga biblioteksfunktionen Seq.concat :
val concat: sequences:seq<#seq<'T>> -> seq<'T>
Du kan skicka någon av följande uppräkningsbara sekvenser till den här funktionen:
- En lista med listor
- En lista över matriser
- En matris med listor
- En matris med sekvenser
- Alla andra kombinationer av uppräkningsbara sekvenser
Följande kod används Seq.concat
för att demonstrera de scenarier som du kan stödja med hjälp av flexibla typer.
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]
let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1
let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]
let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2
let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3
let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4
let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }
let concat5 = Seq.concat [| seq1; seq2; seq3 |]
printfn "%A" concat5
Utdata är följande.
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
I F#, precis som i andra objektorienterade språk, finns det kontexter där härledda typer eller typer som implementerar gränssnitt automatiskt konverteras till en bastyp eller gränssnittstyp. Dessa automatiska konverteringar sker i direkta argument, men inte när typen är i en underordnad position, som en del av en mer komplex typ, till exempel en returtyp av en funktionstyp eller som ett typargument. Därför är den flexibla typ notationen främst användbar när den typ som du tillämpar den på är en del av en mer komplex typ.