Inferencia de tipos con tipos de productos

15

Estoy trabajando en un compilador para un lenguaje concatenante y me gustaría agregar soporte de inferencia de tipos. Entiendo Hindley-Milner, pero he estado aprendiendo la teoría de tipos a medida que avanzo, así que no estoy seguro de cómo adaptarla. ¿El siguiente sistema es sólido y decididamente infeccioso?

Un término es un literal, una composición de términos, una cita de un término o un primitivo.

e::=x|ee|[e]|

Todos los términos denotan funciones. Para dos funciones e1 y e2 , e1e2=e2e1 , es decir, la yuxtaposición denota la composición inversa. Los literales denotan funciones niládicas.

Los términos que no sean composición tienen reglas básicas de tipo:

x:ι[Lit]Γe:σΓ[e]:α.ασ×α[Quot],α not free in Γ

Las reglas de aplicación son notablemente ausentes, ya que los lenguajes concatenantes carecen de ellas.

Un tipo es un literal, una variable de tipo o una función de pilas a pilas, donde una pila se define como una tupla anidada a la derecha. Todas las funciones son implícitamente polimórficas con respecto al "resto de la pila".

τ::=ι|α|ρρρ::=()|τ×ρσ::=τ|α.σ

Esto es lo primero que parece sospechoso, pero no sé exactamente qué tiene de malo.

Para ayudar a la legibilidad y abajo corte en paréntesis, voy a suponer que en esquemas tipo. También usaré una letra mayúscula para una variable que denota una pila, en lugar de un solo valor.ab=b×(a)

Hay seis primitivas. Los primeros cinco son bastante inocuos. duptoma el valor más alto y produce dos copias del mismo. swapcambia el orden de los dos valores superiores. popdescarta el valor superior. quotetoma un valor y produce una cita (función) que lo devuelve. applyaplica una cita a la pila.

dup::Ab.AbAbbswap::Abc.AbcAcbpop::Ab.AbAquote::Ab.AbA(C.CCb)apply::AB.A(AB)B

El último combinador composedebería tomar dos citas y devolver el tipo de su concatenación, es decir, . En el lenguaje concatenativo estáticamente tipadoCat, el tipo dees muy sencillo.[e1][e2]compose=[e1e2]compose

compose::ABCD.A(BC)(CD)A(BD)

Sin embargo, este tipo es demasiado restrictivo: requiere que la producción de la primera función coincida exactamente con el consumo de la segunda. En realidad, debe asumir distintos tipos y luego unificarlos. ¿Pero cómo escribirías ese tipo?

compose::ABCDE.A(BC)(DE)A

Si dejas denotar una diferencia de dos tipos, entonces creo que puedes escribir el tipo correctamente.compose

compose::ABCDE.A(BC)(DE)A((DC)B((CD)E))

Esto es todavía relativamente sencillo: composetoma una función y uno f 2 : D E . Su resultado consume B por encima del consumo de f 2 no producido por f 1 , y produce D por encima de la producción de f 1 no consumido por f 2 . Esto da la regla para la composición ordinaria.f1:BCf2:DEBf2f1Df1f2

Γe1:AB.ABΓe2:CD.CDΓe1e2:((CB)A((BC)D))[Comp]

Sin embargo, no sé si este hipotético realidad corresponde a algo, y lo he estado persiguiendo en círculos durante el tiempo suficiente como para pensar que tomé un giro equivocado. ¿Podría ser una simple diferencia de tuplas?

A.()A=()A.A()=AABCD.ABCD=BD iff A=Cotherwise=undefined

¿Hay algo horriblemente roto sobre esto que no estoy viendo, o estoy en algo como el camino correcto? (Probablemente he cuantificado algunas de estas cosas erróneamente y también agradecería soluciones en esa área).

Jon Purdy
fuente
¿Cómo usas variables en tu gramática? Esta pregunta debería ayudarlo a manejar el "subtipo" que parece necesitar.
jmad
1
@ jmad: no estoy seguro de entender la pregunta. Las variables de tipo están ahí solo para definir esquemas de tipo formalmente, y el lenguaje en sí no tiene variables en absoluto, solo definiciones, que pueden ser recíprocas [mutuamente].
Jon Purdy
composeC=D
twicedup compose apply[1 +] twiceιι[pop] twiceAb.f1,f2:AbAAAbAb.AbbAcompose

Respuestas:

9

compose:ABCδ.δ (α.α AαB) (β.β BβC)δ (γ.γ AγC)

Las letras griegas se usan para las variables del resto de la pila solo por claridad.

B

La verificación de tipos de rango 2 es indecidible en general, creo, aunque se ha realizado un trabajo que da buenos resultados en la práctica (para Haskell):

  • Simon L. Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, Mark Shields: inferencia de tipos práctica para tipos de rango arbitrario. J. Funct. Programa. 17 (1): 1-82 (2007)

La regla de tipo para la composición es simplemente:

Γmi1:α.α UNα siΓmi1:α.α siα CΓmi1 mi2:α.α UNα C

Para que el sistema de tipos funcione en general, necesita la siguiente regla de especialización:

Γmi:α.α UNα siΓmi:α.C UNα C si
Dave Clarke
fuente
Thanks, this was very helpful. This type is correct for functions of a single argument, but it doesn’t support multiple arguments. For instance, dup + should have type ιι because + has type ιιι. But type inference in the absence of annotations is an absolute requirement, so clearly I need to go back to the drawing board. I have an idea for another approach to pursue, though, and will blog about it if it works out.
Jon Purdy
1
The stack types quantify over stack fragments, so there is no problem dealing with two argument functions. I'm not sure how this applies to dup +, as that does not use compose, as you defined it above.
Dave Clarke
Er, right, I meant [dup] [+] compose. But I read αB as B×α; say B=ι×ι; then you have (ι×ι)×α and not ι×(ι×α). The nesting isn’t right, unless you flip the stack around so that the top is the last (deepest nested) element.
Jon Purdy
I may be building my stack in the wrong direction. I don't think the nesting matters, so long as the pairs building up the stack do not appear in the programming language. (I'm planning to update my answer, but need to do a little research first.)
Dave Clarke
Yeah, nesting is pretty much an implementation detail.
Jon Purdy