Le polymorphisme de rang supérieur est extrêmement utile. Dans System F (le langage de base des langages FP typés que vous connaissez), cela est essentiel pour admettre les "encodages Church typés", qui est en fait la façon dont System F fait la programmation. Sans cela, le système F est complètement inutile.
Dans le système F, nous définissons les nombres comme
Nat = forall c. (c -> c) -> c -> c
L'addition a le type
plus : Nat -> Nat -> Nat
plus l r = Λ t. λ (s : t -> t). λ (z : t). l s (r s z)
qui est un type de rang supérieur (le forall c.
apparaît à l'intérieur de ces flèches).
Cela se produit également ailleurs. Par exemple, si vous souhaitez indiquer qu'un calcul est un style de passage de continuation approprié (google "codensity haskell"), vous devez corriger cela comme
type CPSed A = forall c. (A -> c) -> c
Même parler d'un type inhabité dans le système F nécessite un polymorphisme de rang supérieur
type Void = forall a. a
Le long et le court de cela, écrire une fonction dans un système de type pur (système F, CoC) nécessite un polymorphisme de rang supérieur si nous voulons traiter des données intéressantes.
Dans le système F en particulier, ces codages doivent être "imprédicatifs". Cela signifie qu'un forall a.
quantifie absolument tous les types . Cela inclut de manière critique le type même que nous définissons. Dans forall a. a
ce a
pourrait effectivement se présenter à forall a. a
nouveau! Dans des langages comme ML, ce n'est pas le cas, on dit qu'ils sont "prédictifs" puisqu'une variable de type quantifie uniquement sur l'ensemble des types sans quantificateurs (appelés monotypes). Notre définition de l' plus
imprédicativité requise aussi parce que nous avons instancié l' c
in l : Nat
être Nat
!
Enfin, je voudrais mentionner une dernière raison pour laquelle vous aimeriez à la fois l'imprédicativité et le polymorphisme de rang supérieur, même dans un langage avec des types arbitrairement récursifs (contrairement au système F). Dans Haskell, il existe une monade d'effets appelée "monade de threads d'état". L'idée est que la monade du thread d'état vous permet de muter les choses mais nécessite d'y échapper pour que votre résultat ne dépende de rien de mutable. Cela signifie que les calculs ST sont remarquablement purs. Pour appliquer cette exigence, nous utilisons un polymorphisme de rang supérieur
runST :: forall a. (forall s. ST s a) -> a
Ici, en veillant à ce qu'il a
soit lié en dehors de la portée où nous introduisons s
, nous savons que a
représente un type bien formé qui ne dépend pas s
. Nous utilisons s
pour paramétrer toutes les choses mutables dans ce thread d'état particulier afin que nous sachions que cela a
est indépendant des choses mutables et donc que rien n'échappe à la portée de ce ST
calcul! Un merveilleux exemple d'utilisation de types pour exclure les programmes mal formés.
Soit dit en passant, si vous souhaitez en savoir plus sur la théorie des types, je vous suggère d'investir dans un bon livre ou deux. Il est difficile d'apprendre ces trucs en morceaux. Je suggérerais l'un des livres de Pierce ou Harper sur la théorie du PL en général (et certains éléments de la théorie des types). Le livre "Sujets avancés en types et langages de programmation" couvre également une bonne partie de la théorie des types. Enfin, "Programmation dans la théorie des types de Martin Lof" est une très bonne exposition de la théorie des types intensionnelle que Martin Lof a décrite.
let sdff = (g : (f : <T> (e : T) => void) => void) => {}