implementación de inferencia de tipos

91

Veo algunas discusiones interesantes aquí sobre la escritura estática frente a la dinámica. Generalmente prefiero la escritura estática, debido a la verificación de tipos de compilación, código mejor documentado, etc. Sin embargo, estoy de acuerdo en que desordenan el código si se hace de la manera en que lo hace Java, por ejemplo.

Así que estoy a punto de comenzar a construir un lenguaje de estilo funcional propio, y la inferencia de tipos es una de las cosas que quiero implementar. Entiendo que es un gran tema, y ​​no estoy tratando de crear algo que no se haya hecho antes, solo una inferencia básica ...

¿Algún consejo sobre qué leer que me ayude con esto? Preferiblemente, algo más pragmático / práctico en lugar de textos de teoría de categorías / teoría de tipos más teóricos. Si hay un texto de discusión de implementación, con estructuras de datos / algoritmos, sería maravilloso.

azul profundo
fuente
1
¡Exactamente la pregunta que estaba buscando, con excelentes respuestas!
Paul Hollingsworth

Respuestas:

90

Encontré los siguientes recursos útiles para comprender la inferencia de tipos, en orden de dificultad creciente:

  1. El capítulo 30 (Inferencia de tipos) del libro PLAI , Lenguajes de programación: aplicación e interpretación , disponible gratuitamente , describe la inferencia de tipos basada en unificación.
  2. El curso de verano Interpretación de tipos como valores abstractos presenta elegantes evaluadores, verificadores de tipos, reconstructores de tipos e inferenciadores que utilizan Haskell como metalenguaje.
  3. Capítulo 7 (Tipos) del libro EOPL , Fundamentos de los lenguajes de programación .
  4. Capítulo 22 (Reconstrucción de tipos) del libro TAPL , Tipos y lenguajes de programación , y las correspondientes implementaciones OCaml recon y fullrecon .
  5. Capítulo 13 (Reconstrucción de tipos) del nuevo libro DCPL , Conceptos de diseño en lenguajes de programación .
  6. Selección de trabajos académicos .
  7. TypeInference del compilador de cierre es un ejemplo del enfoque de análisis de flujo de datos para la inferencia de tipos, que se adapta mejor a los lenguajes dinámicos que el enfoque Hindler Milner.

Sin embargo, dado que la mejor manera de aprender es hacerlo, sugiero encarecidamente implementar la inferencia de tipos para un lenguaje funcional de juguete trabajando a través de una tarea de un curso de lenguajes de programación.

Recomiendo estas dos tareas accesibles en ML, que ambos pueden completar en menos de un día:

  1. Intérprete PCF ( una solución ) para calentar.
  2. Inferencia de tipo PCF ( una solución ) para implementar el algoritmo W para la inferencia de tipo Hindley-Milner.

Estas asignaciones son de un curso más avanzado:

  1. Implementando MiniML

  2. Tipos polimórficos, existenciales y recursivos (PDF)

  3. Comprobación de tipo bidireccional (PDF)

  4. Subtipado y objetos (PDF)

namin
fuente
2
¿Soy solo yo, o la descripción PLAI es en gran medida incorrecta / incompleta? La conferencia le agrega un poco más, pero aparentemente no es suficiente para que funcione.
Rei Miyasaka
Tampoco pude obtener el algoritmo en la versión 2012 del libro PLAI. No hay sustituciones en la lista de restricciones. En cambio, implementé el algoritmo de inferencia de tipos en la versión 2003-7 del libro PLAI, parece funcionar mejor y también escala bien para permitir polimorfismo.
heykell
28

Es lamentable que mucha de la literatura sobre el tema sea muy densa. Yo también estaba en tus zapatos. Recibí mi primera introducción al tema de Lenguajes de programación: aplicaciones e interpretación.

http://www.plai.org/

Intentaré resumir la idea abstracta seguida de detalles que no encontré inmediatamente obvios. Primero, se puede pensar en la inferencia de tipos para generar y luego resolver restricciones. Para generar restricciones, recorre el árbol de sintaxis y genera una o más restricciones en cada nodo. Por ejemplo, si el nodo es un +operador, los operandos y los resultados deben ser todos números. Un nodo que aplica una función tiene el mismo tipo que el resultado de la función, y así sucesivamente.

Para un idioma sin let, puede resolver ciegamente las restricciones anteriores mediante sustitución. Por ejemplo:

(if (= 1 2) 
    1 
    2)

aquí, podemos decir que la condición de la instrucción if debe ser booleana, y que el tipo de instrucción if es el mismo que el tipo de sus cláusulas theny else. Como sabemos 1y 2somos números, por sustitución, sabemos que el ifenunciado es un número.

Donde las cosas se ponen feas, y lo que no pude entender por un tiempo, es lidiar con dejar:

(let ((id (lambda (x) x)))
    (id id))

Aquí, nos hemos vinculado ida una función que devuelve lo que haya pasado, también conocida como función de identidad. El problema es que el tipo de parámetro de la función xes diferente en cada uso de id. La segunda ides una función de tipo a -> a, donde apuede haber cualquier cosa. El primero es de tipo (a -> a) -> (a -> a). Esto se conoce como polimorfismo let. La clave es resolver las restricciones en un orden particular: primero resuelva las restricciones para la definición de id. Esto será a -> a. Luego, idse pueden sustituir copias nuevas y separadas del tipo de en las restricciones para cada lugar id, por ejemplo a2 -> a2y a3 -> a3.

Eso no se explicó fácilmente en los recursos en línea. Mencionarán el algoritmo W o M, pero no cómo funcionan en términos de resolución de restricciones, o por qué no se burla del polimorfismo let: cada uno de esos algoritmos impone un orden para resolver las restricciones.

Encontré este recurso extremadamente útil para vincular el algoritmo W, M y el concepto general de generación de restricciones y resolución de todos juntos. Es un poco denso, pero mejor que muchos:

http://www.cs.uu.nl/research/techreps/repo/CS-2002/2002-031.pdf

Muchos de los otros artículos también son buenos:

http://people.cs.uu.nl/bastiaan/papers.html

Espero que ayude a aclarar un mundo algo turbio.

Pablo
fuente
7

Además de Hindley Milner para lenguajes funcionales, otro enfoque popular para la inferencia de tipos para lenguaje dinámico es abstract interpretation.

La idea de la interpretación abstracta es escribir un intérprete especial para el lenguaje, en lugar de mantener un entorno de valores concretos (1, falso, cierre), funciona con valores abstractos, también conocidos como tipos (int, bool, etc.). Dado que está interpretando el programa en valores abstractos, por eso se llama interpretación abstracta.

Pysonar2 es una elegante implementación de interpretación abstracta para Python. Google lo utiliza para analizar proyectos de Python. Básicamente, se utiliza visitor patternpara enviar una llamada de evaluación al nodo AST relevante. La función de visitante transform acepta el contexten el que se evaluará el nodo actual y devuelve el tipo de nodo actual.

Wei Chen
fuente
4

Tipos y lenguajes de programación por Benjamin C. Pierce

Scott Wisniewski
fuente
3

Lambda the Ultimate, comenzando aquí .

Doug Currie
fuente