Il polimorfismo di rango superiore è estremamente utile. Nel Sistema F (il linguaggio di base dei linguaggi FP tipizzati che conosci), questo è essenziale per ammettere "codifiche della Chiesa tipizzate", che è in realtà il modo in cui il Sistema F esegue la programmazione. Senza questi, il sistema F è completamente inutile.
Nel Sistema F, definiamo i numeri come
Nat = forall c. (c -> c) -> c -> c
L'aggiunta ha il tipo
plus : Nat -> Nat -> Nat
plus l r = Λ t. λ (s : t -> t). λ (z : t). l s (r s z)
che è un tipo di rango superiore ( forall c.
appare all'interno di quelle frecce).
Questo succede anche in altri posti. Ad esempio, se vuoi indicare che un calcolo è uno stile di passaggio di continuazione adeguato (google "codensity haskell"), lo avresti corretto come
type CPSed A = forall c. (A -> c) -> c
Anche parlare di un tipo disabitato nel Sistema F richiede un polimorfismo di rango superiore
type Void = forall a. a
Il lungo e il corto di questo, scrivere una funzione in un sistema di tipo puro (Sistema F, CoC) richiede un polimorfismo di rango superiore se vogliamo trattare dati interessanti.
Nel sistema F in particolare, queste codifiche devono essere "impredicative". Ciò significa che un si forall a.
quantifica assolutamente tutti i tipi . Ciò include in modo critico il tipo che stiamo definendo. In forall a. a
questo a
potrebbe effettivamente rappresentare di forall a. a
nuovo! In linguaggi come ML non è questo il caso, si dice che siano "predicativi" poiché una variabile di tipo si quantifica solo sull'insieme di tipi senza quantificatori (chiamati monotipi). La nostra definizione di plus
impredicativity necessaria anche perché abbiamo istanziato l' c
in l : Nat
essere Nat
!
Infine, vorrei menzionare un'ultima ragione per cui si desidera sia l'impredicatività che il polimorfismo di rango superiore anche in una lingua con tipi arbitrariamente ricorsivi (a differenza del Sistema F). In Haskell, esiste una monade per effetti chiamata "monade thread di stato". L'idea è che la monade del thread di stato ti permetta di mutare le cose, ma richiede di sfuggire che il tuo risultato non dipende da nulla di mutabile. Ciò significa che i calcoli ST sono notevolmente puri. Per applicare questo requisito utilizziamo un polimorfismo di rango superiore
runST :: forall a. (forall s. ST s a) -> a
Qui assicurandoci che a
sia limitato al di fuori dell'ambito in cui vi presentiamo s
, sappiamo che a
sta per un tipo ben formato su cui non si basa s
. Usiamo s
per paramerizzare tutte le cose mutabili in quel particolare filo di stato, quindi sappiamo che a
è indipendente dalle cose mutabili e quindi che nulla sfugge all'ambito di tale ST
calcolo! Un meraviglioso esempio dell'uso dei tipi per escludere programmi mal formati.
A proposito, se sei interessato a conoscere la teoria dei tipi, suggerirei di investire in un buon libro o due. È difficile imparare queste cose a pezzi. Suggerirei uno dei libri di Pierce o Harper sulla teoria del PL in generale (e alcuni elementi della teoria dei tipi). Il libro "Argomenti avanzati in tipi e linguaggi di programmazione" copre anche una buona parte della teoria dei tipi. Infine, "Programmare nella teoria dei tipi di Martin Lof" è un'ottima esposizione alla teoria del tipo intensionale delineata da Martin Lof.
let sdff = (g : (f : <T> (e : T) => void) => void) => {}