Tipi flessibili
Un'annotazione di tipo flessibile indica che un parametro, una variabile o un valore ha un tipo compatibile con un tipo specificato, in cui la compatibilità è determinata dalla posizione in una gerarchia orientata agli oggetti di classi o interfacce. I tipi flessibili sono utili in particolare quando la conversione automatica in tipi superiori nella gerarchia dei tipi non si verifica, ma si vuole comunque abilitare la funzionalità per funzionare con qualsiasi tipo nella gerarchia o con qualsiasi tipo che implementa un'interfaccia.
Sintassi
#type
Osservazioni:
Nella sintassi precedente il tipo rappresenta un tipo di base o un'interfaccia.
Un tipo flessibile equivale a un tipo generico con un vincolo che limita i tipi consentiti ai tipi compatibili con il tipo di interfaccia o di base. Ovvero, le due righe di codice seguenti sono equivalenti.
#SomeType
'T when 'T :> SomeType
I tipi flessibili sono utili in diversi tipi di situazioni. Ad esempio, quando si dispone di una funzione di ordine superiore (una funzione che accetta una funzione come argomento), è spesso utile fare in modo che la funzione restituisca un tipo flessibile. Nell'esempio seguente l'uso di un tipo flessibile con un argomento sequenza in iterate2
consente alla funzione di ordine superiore di usare funzioni che generano sequenze, matrici, elenchi e qualsiasi altro tipo enumerabile.
Si considerino le due funzioni seguenti, una delle quali restituisce una sequenza, l'altra della quale restituisce un tipo flessibile.
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])
Come altro esempio, si consideri la funzione di libreria Seq.concat :
val concat: sequences:seq<#seq<'T>> -> seq<'T>
È possibile passare una delle sequenze enumerabili seguenti a questa funzione:
- Elenco di elenchi
- Elenco di matrici
- Matrice di elenchi
- Matrice di sequenze
- Qualsiasi altra combinazione di sequenze enumerabili
Il codice seguente usa per illustrare Seq.concat
gli scenari che è possibile supportare usando tipi flessibili.
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
L'output è indicato di seguito.
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; ...]
In F#, come in altri linguaggi orientati agli oggetti, esistono contesti in cui i tipi o i tipi derivati che implementano le interfacce vengono convertiti automaticamente in un tipo di base o un tipo di interfaccia. Queste conversioni automatiche si verificano in argomenti diretti, ma non quando il tipo si trova in una posizione subordinata, come parte di un tipo più complesso, ad esempio un tipo restituito di un tipo di funzione o come argomento di tipo. Pertanto, la notazione del tipo flessibile è particolarmente utile quando il tipo a cui viene applicato fa parte di un tipo più complesso.