implementando inferência de tipo

91

Vejo algumas discussões interessantes aqui sobre tipagem estática x dinâmica. Eu geralmente prefiro a tipagem estática, devido à verificação de tipo de compilação, código melhor documentado, etc. No entanto, eu concordo que eles bagunçam o código se feitos da maneira que o Java faz, por exemplo.

Portanto, estou prestes a começar a construir minha própria linguagem de estilo funcional, e a inferência de tipos é uma das coisas que desejo implementar. Eu entendo que é um grande assunto, e não estou tentando criar algo que não tenha sido feito antes, apenas inferência básica ...

Alguma sugestão sobre o que ler que me ajudará com isso? De preferência, algo mais pragmático / prático em oposição a textos mais teóricos da teoria das categorias / teoria dos tipos. Se houver um texto de discussão de implementação por aí, com estruturas de dados / algoritmos, isso seria ótimo.

azul profundo
fonte
1
Exatamente a pergunta que eu estava procurando, com ótimas respostas!
Paul Hollingsworth

Respostas:

90

Achei os seguintes recursos úteis para entender a inferência de tipo, em ordem crescente de dificuldade:

  1. Capítulo 30 (Inferência de Tipos) do livro disponível gratuitamente PLAI , Linguagens de Programação: Aplicação e Interpretação , esboça a inferência de tipo baseada na unificação.
  2. O curso de verão Interpretando tipos como valores abstratos apresenta avaliadores elegantes, verificadores de tipo, reconstrutores de tipo e inferenciadores usando Haskell como metalinguagem.
  3. Capítulo 7 (Tipos) do livro EOPL , Essentials of Programming Languages .
  4. Capítulo 22 (Reconstrução de Tipo) do livro TAPL , Tipos e Linguagens de Programação , e as implementações OCaml correspondentes recon e fullrecon .
  5. Capítulo 13 (Type Reconstruction) do novo livro DCPL , Design Concepts in Programming Languages .
  6. Seleção de trabalhos acadêmicos .
  7. TypeInference do compilador de Closure é um exemplo da abordagem de análise de fluxo de dados para inferência de tipo, que é mais adequada para linguagens dinâmicas que a abordagem de Hindler Milner.

No entanto, como a melhor maneira de aprender é fazendo, sugiro fortemente a implementação de inferência de tipo para uma linguagem funcional de brinquedo, trabalhando em uma tarefa de casa de um curso de linguagens de programação.

Eu recomendo estes dois trabalhos de casa acessíveis em ML, que você pode concluir em menos de um dia:

  1. Intérprete PCF ( uma solução ) para aquecer.
  2. PCF Type Inference ( uma solução ) para implementar o algoritmo W para a inferência de tipo Hindley-Milner.

Essas tarefas são de um curso mais avançado:

  1. Implementando MiniML

  2. Tipos polimórficos, existenciais e recursivos (PDF)

  3. Verificação de tipo bidirecional (PDF)

  4. Subtipagem e objetos (PDF)

namin
fonte
2
Sou apenas eu ou a descrição PLAI está amplamente incorreta / incompleta? A palestra acrescenta um pouco mais a isso, mas ainda aparentemente não o suficiente para fazê-la funcionar.
Rei Miyasaka de
Eu também não consegui obter o algoritmo na versão 2012 do livro PLAI. Não há substituições na lista de restrições. Em vez disso, implementei o algoritmo de inferência de tipo na versão 2003-7 do livro PLAI, parece funcionar melhor e escalar bem para o polimorfismo let.
heykell
28

É uma pena que grande parte da literatura sobre o assunto seja muito densa. Eu também estava no seu lugar. Tive minha primeira introdução ao assunto em Linguagens de Programação: Aplicações e Interpretação

http://www.plai.org/

Tentarei resumir a ideia abstrata seguida de detalhes que não achei imediatamente óbvios. Em primeiro lugar, a inferência de tipo pode ser pensada para gerar e, em seguida, resolver restrições. Para gerar restrições, você percorre novamente a árvore de sintaxe e gera uma ou mais restrições em cada nó. Por exemplo, se o nó for um+ operador, os operandos e os resultados devem ser todos números. Um nó que aplica uma função tem o mesmo tipo que o resultado da função e assim por diante.

Para uma linguagem sem let, você pode resolver cegamente as restrições acima por substituição. Por exemplo:

(if (= 1 2) 
    1 
    2)

aqui, podemos dizer que a condição da instrução if deve ser booleana e que o tipo da instrução if é o mesmo que o tipo de suas cláusulas thene else. Visto que sabemos 1e 2somos números, por substituição, sabemos que a ifafirmação é um número.

Onde as coisas ficam feias, e o que eu não conseguia entender por um tempo, é lidar com isso:

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

Aqui, associamos ida uma função que retorna tudo o que você passou, também conhecido como função de identidade. O problema é que o tipo de parâmetro da função xé diferente em cada uso de id. A segunda idé uma função do tipo a -> a, onde apode ser qualquer coisa. O primeiro é do tipo (a -> a) -> (a -> a). Isso é conhecido como polimorfismo let. A chave é resolver as restrições em uma ordem particular: primeiro resolva as restrições para a definição de id. Isso vai ser a -> a. Em seguida, cópias novas e separadas do tipo de idpodem ser substituídas nas restrições de cada local id, por exemplo a2 -> a2e a3 -> a3.

Isso não foi explicado prontamente nos recursos online. Eles mencionarão o algoritmo W ou M, mas não como eles funcionam em termos de resolução de restrições, ou por que ele não vomita no polimorfismo let: cada um desses algoritmos impõe uma ordem na resolução das restrições.

Achei este recurso extremamente útil para amarrar o Algoritmo W, M e o conceito geral de geração de restrição e resolução de todos juntos. É um pouco denso, mas melhor do que muitos:

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

Muitos dos outros papéis também são bons:

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

Espero que ajude a esclarecer um mundo um tanto obscuro.

Paulo
fonte
7

Além de Hindley Milner para linguagens funcionais, outra abordagem popular para inferência de tipos para linguagem dinâmica é abstract interpretation.

A ideia da interpretação abstrata é escrever um intérprete especial para a linguagem, ao invés de manter um ambiente de valores concretos (1, false, closure), funciona em valores abstratos, também conhecidos como tipos (int, bool, etc). Como ele interpreta o programa em valores abstratos, é por isso que é chamado de interpretação abstrata.

Pysonar2 é uma implementação elegante de interpretação abstrata para Python. Ele é usado pelo Google para analisar projetos Python. Basicamente, ele usa visitor patternpara despachar a chamada de avaliação para o nó AST relevante. A função de visitante transform aceita o contextno qual o nó atual será avaliado e retorna o tipo de nó atual.

Wei Chen
fonte
4

Tipos e linguagens de programação de Benjamin C. Pierce

Scott Wisniewski
fonte
3

Lambda the Ultimate, começando aqui .

Doug Currie
fonte