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=e2∘e1 , 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.Ab→Abbswap::∀Abc.Abc→Acbpop::∀Ab.Ab→Aquote::∀Ab.Ab→A(∀C.C→Cb)apply::∀AB.A(A→B)→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(B→C)(C→D)→A(B→D)

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(B→C)(D→E)→A…

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

compose::∀ABCDE.A(B→C)(D→E)→A((D∖C)B→((C∖D)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:B→Cf2:D→EBf2f1Df1f2

Γ⊢e1:∀AB.A→BΓ⊢e2:∀CD.C→DΓ⊢e1e2:((C∖B)A→((B∖C)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∖()=A∀ABCD.AB∖CD=B∖D 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).


¿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] twice∀Ab.f1,f2:Ab→AA≠Ab∀Ab.Abb→Acompose

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.