Estoy bastante seguro de que todos están familiarizados con los métodos genéricos del formulario:
T DoSomething<T>(T item)
Esta función también se llama paramétricamente polimórfica (PP), específicamente rango-1 PP.
Digamos que este método se puede representar usando un objeto de función de la forma:
<T> : T -> T
Es decir, <T>
significa que toma un parámetro de tipo y T -> T
significa que toma un parámetro de tipo T
y devuelve un valor del mismo tipo.
Entonces lo siguiente sería una función PP de rango 2:
(<T> : T -> T) -> int
La función no toma parámetros de tipo en sí, pero toma una función que toma un parámetro de tipo. Puede continuar esto iterativamente, haciendo que el anidamiento sea cada vez más profundo, obteniendo PP de rango cada vez más alto.
Esta característica es realmente rara entre los lenguajes de programación. Incluso Haskell no lo permite por defecto.
¿Es útil? ¿Puede describir comportamientos que son difíciles de describir de otra manera?
Además, ¿qué significa que algo sea impredecible ? (en este contexto)
let sdff = (g : (f : <T> (e : T) => void) => void) => {}
Respuestas:
En general, utiliza un polimorfismo de rango superior cuando desea que la persona que llama pueda seleccionar el valor de un parámetro de tipo, en lugar de la persona que llama . Por ejemplo:
Cualquier función
g
que le pase a estof
debe poder darme unInt
valor de algún tipo, donde lo único que seg
sabe sobre ese tipo es que tiene una instancia deShow
. Entonces estos son kosher:Pero estos no son:
Una aplicación particularmente útil es usar el alcance de tipos para aplicar el alcance de los valores . Supongamos que tenemos un objeto de tipo
Action<T>
, que representa una acción que podemos ejecutar para producir un resultado de tipoT
, como un futuro o una devolución de llamada.Ahora, supongamos que también tenemos un
Action
que puede asignarResource<T>
objetos:Queremos asegurar que esos recursos solo se usen dentro del lugar
Action
donde se crearon, y no se compartan entre diferentes acciones o diferentes ejecuciones de la misma acción, de modo que las acciones sean deterministas y repetibles.Podemos utilizar tipos de mayor rango para lograr esto agregando un parámetro
S
a los tiposResource
yAction
, que es totalmente abstracto: representa el "alcance" deAction
. Ahora nuestras firmas son:Ahora, cuando damos
runAction
unAction<S, T>
, estamos seguros de que debido a que el parámetro "alcance"S
es completamente polimórfico, no puede escapar del cuerpo derunAction
, por lo tanto , ¡cualquier valor de un tipo que se utilizaS
comoResource<S, int>
tampoco puede escapar!(En Haskell, esto se conoce como la
ST
mónada, donderunAction
se llamarunST
,Resource
se llamaSTRef
ynewResource
se llamanewSTRef
).fuente
ST
mónada es un ejemplo muy interesante. ¿Puedes dar más ejemplos de cuándo sería útil el polimorfismo de rango superior?data Fetch d = forall a. Fetch (d a) (MVar a)
, que es un par de solicitudes a una fuente de datosd
y un espacio en el que almacenar el resultado. El resultado y el espacio deben tener tipos coincidentes, pero ese tipo está oculto, por lo que puede tener una lista heterogénea de solicitudes al mismo origen de datos. Ahora se puede utilizar el polimorfismo de rango superior que escribir una función que recupera todas las solicitudes, dada una función que obtiene uno:fetch :: (forall a. d a -> IO a) -> [Fetch d] -> IO ()
.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
La adición tiene el tipo
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
Incluso hablar de un tipo deshabitado en el Sistema F requiere un polimorfismo de rango superior
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. Enforall a. a
eso ena
realidad podría significar deforall 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 deplus
impredicativity requerida, así porque la instanciac
enl : Nat
que serNat
!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
Aquí, al asegurarnos de que
a
está fuera del alcance donde presentamoss
, sabemos quea
representa un tipo bien formado que no dependes
. ¡Usamoss
parameritizar todas las cosas mutables en ese hilo de estado particular para que sepamos quea
es independiente de las cosas mutables y, por lo tanto, que nada escapa al alcance de eseST
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ó.
fuente