He pensado un poco en esto. El problema principal es que, en general, no sabemos qué tan grande es un valor de tipo polimórfico. Si no tiene esta información, debe obtenerla de alguna manera. La monomorfización obtiene esta información para usted al especializar el polimorfismo. El boxeo obtiene esta información para usted al poner todo en una representación de tamaño conocido.
Una tercera alternativa es hacer un seguimiento de esta información en los tipos. Básicamente, lo que puede hacer es introducir un tipo diferente para cada tamaño de datos, y luego las funciones polimórficas se pueden definir sobre todos los tipos de un tamaño particular. Dibujaré un sistema de este tipo a continuación.
TiposConstructores tipoκUNA: : = N: : =El |∀ a : κ .UNAEl |αEl |A × BEl |A + BEl |A → Br e fUNAEl |P a d ( k )El |μ α : κ .UNA
Aquí, la idea de alto nivel es que el tipo de tipo te dice cuántas palabras se necesitan para colocar un objeto en la memoria. Para cualquier tamaño dado, es fácil ser polimórfico sobre todos los tipos de ese tamaño en particular. Dado que cada tipo, incluso los polimórficos, todavía tiene un tamaño conocido, la compilación no es más difícil de lo que es para C.
Las reglas de clasificación convierten este inglés en matemáticas, y deberían verse así:
α : n ∈ ΓΓ ⊢ α : nΓ , α : n ⊢ A : mΓ ⊢ ∀ α : n .A : m
Γ ⊢ A : nΓ ⊢ B : mΓ ⊢ A × B : n + mΓ ⊢ A : nΓ ⊢ B : nΓ ⊢ A + B : n + 1
Γ ⊢ A : mΓ ⊢ B : nΓ ⊢ A → B : 1Γ ⊢ A : nΓ ⊢ r e fA : 1
Γ ⊢ P a d ( k ) : kΓ , α : n ⊢ A : nΓ ⊢ μ α : n .A : n
Por lo tanto, el cuantificador general requiere que proporcione el tipo que está buscando. Del mismo modo, el emparejamiento es un tipo de par sin caja, que simplemente presenta una junto a una en la memoria (como un tipo de estructura C). Las uniones disjuntas toman dos valores del mismo tamaño y luego agregan una palabra para una etiqueta discriminadora. Las funciones son cierres, representados como de costumbre por un puntero a un registro del entorno y el código.A × BUNAsi
Las referencias son interesantes: los punteros son siempre una palabra, pero pueden apuntar a valores de cualquier tamaño. Esto permite a los programadores implementar el
polimorfismo en objetos arbitrarios mediante el boxeo, pero no requiere
que lo hagan. Finalmente, una vez que los tamaños explícitos están en juego, a menudo es útil introducir un tipo de relleno, que usa espacio pero no hace nada. (Entonces, si desea tomar la unión disjunta de un int y un par de entradas, necesitará agregar relleno al primer int, para que el diseño del objeto sea uniforme).
Los tipos recursivos tienen la regla de formación estándar, pero tenga en cuenta que las ocurrencias recursivas tienen que ser del mismo tamaño, lo que significa que generalmente tiene que pegarlas en un puntero para que la clasificación funcione. Por ejemplo, el tipo de datos de la lista podría representarse como
μ α : 1.r e f( P a d ( 2 ) + i n t × α )
Entonces, esto apunta a un valor de lista vacío, o un par de un int y un puntero a otra lista vinculada.
La verificación de tipos para sistemas como este tampoco es muy difícil; el algoritmo en mi documento ICFP con Joshua Dunfield, la verificación de tipo bidireccional completa y fácil para el polimorfismo de rango superior se aplica a este caso casi sin cambios.