El polimorfismo de rango superior es extremadamente útil. En el Sistema F (el idioma central de los lenguajes de FP mecanografiados con los que está familiarizado), esto es esencial para admitir las "codificaciones de la Iglesia mecanografiadas", que en realidad es la forma en que el Sistema F programa. Sin estos, el sistema F es completamente inútil.
En el Sistema F, definimos los números como
Nat = forall c. (c -> c) -> c -> c
La adición tiene el tipo
plus : Nat -> Nat -> Nat
plus l r = Λ t. λ (s : t -> t). λ (z : t). l s (r s z)
que es un tipo de rango más alto ( forall c.
aparece dentro de esas flechas).
Esto surge en otros lugares también. Por ejemplo, si desea indicar que un cálculo es un estilo de paso de continuación adecuado ("codensity haskell" de Google), entonces debe corregir esto como
type CPSed A = forall c. (A -> c) -> c
Incluso hablar de un tipo deshabitado en el Sistema F requiere un polimorfismo de rango superior
type Void = forall a. a
En resumidas cuentas, escribir una función en un sistema de tipo puro (Sistema F, CoC) requiere un polimorfismo de rango superior si queremos tratar con datos interesantes.
En el Sistema F en particular, estas codificaciones deben ser "impredecibles". Esto significa que a forall a.
cuantifica absolutamente todos los tipos . Esto incluye críticamente el mismo tipo que estamos definiendo. En forall a. a
eso en a
realidad podría significar de forall a. a
nuevo! En lenguajes como ML este no es el caso, se dice que son "predicativos" ya que una variable de tipo cuantifica solo sobre el conjunto de tipos sin cuantificadores (llamados monotipos). Nuestra definición de plus
impredicativity requerida, así porque la instancia c
en l : Nat
que ser Nat
!
Finalmente, me gustaría mencionar una última razón en la que le gustaría tanto la impredicatividad como el polimorfismo de rango superior incluso en un lenguaje con tipos arbitrariamente recursivos (a diferencia del Sistema F). En Haskell, hay una mónada para los efectos llamada "mónada de hilos de estado". La idea es que la mónada de subprocesos de estado le permite mutar cosas, pero requiere escapar para que su resultado no dependa de nada mutable. Esto significa que los cálculos de ST son observablemente puros. Para hacer cumplir este requisito, utilizamos polimorfismo de rango superior
runST :: forall a. (forall s. ST s a) -> a
Aquí, al asegurarnos de que a
está fuera del alcance donde presentamos s
, sabemos que a
representa un tipo bien formado que no depende s
. ¡Usamos s
parameritizar todas las cosas mutables en ese hilo de estado particular para que sepamos que a
es independiente de las cosas mutables y, por lo tanto, que nada escapa al alcance de ese ST
cálculo! Un maravilloso ejemplo del uso de tipos para descartar programas mal formados.
Por cierto, si está interesado en aprender sobre la teoría de tipos, le sugiero que invierta en un buen libro o dos. Es difícil aprender estas cosas en pedazos. Sugeriría uno de los libros de Pierce o Harper sobre teoría PL en general (y algunos elementos de la teoría de tipos). El libro "Temas avanzados en tipos y lenguajes de programación" también cubre una buena cantidad de teoría de tipos. Finalmente, "Programación en la teoría de tipos de Martin Lof" es una muy buena exposición de la teoría de tipos intensiva que Martin Lof describió.
let sdff = (g : (f : <T> (e : T) => void) => void) => {}