O que uma prova de correção para um verificador tipográfico está realmente provando?

11

Venho programando há vários anos, mas não estou familiarizado com o CS teórico. Recentemente, tenho tentado estudar linguagens de programação e, como parte disso, verificação de tipo e inferência.

Minha pergunta é: se eu tentar escrever um programa de inferência e verificação de tipo para uma linguagem de programação e desejar provar que meu datilógrafo funciona, qual é exatamente a prova que estou procurando?

Em linguagem simples, desejo que meu verificador de tipos consiga identificar quaisquer erros em um pedaço de código que possam ocorrer no tempo de execução. Se eu usasse algo como Coq para tentar provar que minha implementação está correta, o que exatamente essa "prova de correção" tentará mostrar?

Vivek Ghaisas
fonte
Talvez você possa esclarecer se deseja saber (1) se sua implementação implementa um determinado sistema de digitação ou (2) se seu sistema de digitação T evita os erros que você acha que deveria? São perguntas diferentes. TT
Martin Berger
11
@MartinBerger: Ah, parece que pulei essa diferença. Minha pergunta real provavelmente pretendia perguntar aos dois. O contexto é que estou tentando criar uma linguagem e, para isso, escrevi um typechecker. E as pessoas me pediram para usar um algoritmo testado e comprovado. Eu estava interessado em ver como seria difícil "provar" o algoritmo e o checador de tipos que eu estava usando estavam "corretos". Daí a ambiguidade na minha pergunta.
Vivek Ghaisas
2
(1) é realmente uma questão de verificação de programa e tem pouco a ver com digitação. Só precisa mostrar que sua implementação atende às suas especificações. Quanto a (2), primeiro defina o que significa ser um erro de tipo imediato (por exemplo, termos como 2 + "hello"esse são 'presos'). Depois que isso for formalizado, você poderá provar o teorema da solidez do tipo. Isso significa que nenhum programa tipável pode evoluir para um erro de tipo imediato. Formalmente, você prova que, se um programa for digitado, e para qualquer n : se M executar n etapas para se tornar N , N não terá um erro de tipo imediato. (1/2)MnMnNN
Martin Berger
11
Isso geralmente é comprovado por indução em na derivação do julgamento de digitação. (2/2)n
Martin Berger
Obrigado! Com base na sua explicação, parece que (2) é realmente o que eu estava procurando. Você poderia fazer uma resposta? (E talvez adicione alguns detalhes que julgue úteis.) Aceitaria isso como resposta! :)
Vivek Ghaisas

Respostas:

10

A questão pode ser interpretada de duas maneiras:

  • Se a implementação implementa um determinado sistema de digitação ?T
  • T

O primeiro é realmente uma questão de verificação de programa e tem pouco a ver com digitação. Só precisa mostrar que sua implementação atende às suas especificações, veja a resposta de Andrej.

TT

  • Primeiro, você define formalmente o que significa para um programa ter um erro de digitação imediato . Há muitas maneiras de definir isso - depende de você. Normalmente, queremos impedir programas como o 2 + "hello". Em outras palavras, você precisa definir um subconjunto de programas, chamado Bad , que contém exatamente os programas com erro de digitação imediato.

  • ΓM:α.MαΓ

    ΓM:αMNN

    Como provar esse teorema depende dos detalhes do idioma, do sistema de digitação e da sua escolha de Mau .

MMNM

  • ΓM:αMNΓN:α

  • ΓM:αM

Observe que nem todos os sistemas de digitação têm "redução de assunto", por exemplo, tipos de sessão. Nesse caso, são necessárias técnicas de prova mais sofisticadas.

Martin Berger
fonte
20

Esta é uma boa pergunta! Ele pergunta o que esperamos dos tipos em um idioma digitado.

Primeiro, observe que podemos digitar qualquer linguagem de programação com o unitype : basta escolher uma letra, digamos Ue dizer que todo programa tem tipo U. Isso não é muito útil, mas faz um ponto.

eAeAAint

Não há fim para quão expressivos seus tipos podem ser. Em princípio, eles podem ser qualquer tipo de afirmação lógica, podem usar teoria das categorias e outros enfeites etc. Por exemplo, tipos dependentes permitem expressar coisas como "essa função mapeia listas para listar, de modo que a saída seja uma entrada classificada". Você pode ir além, no momento, estou ouvindo uma palestra sobre "lógicas de separação simultâneas", que permite falar sobre como os programas simultâneos funcionam com o estado compartilhado. Coisa chique.

A arte dos tipos no design da linguagem de programação é a de equilibrar expressividade e simplicidade :

  • tipos mais expressivos nos permitem explicar com mais detalhes (a nós mesmos e ao compilador) o que deveria estar acontecendo
  • tipos mais simples são mais fáceis de entender e podem ser automatizados mais facilmente no compilador. (As pessoas criam tipos que requerem essencialmente um assistente de prova e a entrada do usuário para fazer a verificação do tipo.)

A simplicidade não deve ser subestimada, pois nem todo programador possui um doutorado em teoria das linguagens de programação.

Então, voltemos à sua pergunta: como você sabe que seu sistema de tipos é bom ? Bem, prove os teoremas que mostram que seus tipos estão equilibrados. Haverá dois tipos de teoremas:

  1. Teoremas que dizem que seus tipos são úteis . Saber que um programa tem um tipo deve implicar algumas garantias, por exemplo, que o programa não ficará preso (isso seria um teorema de Segurança ). Outra família de teoremas conectaria os tipos a modelos semânticos, para que possamos começar a usar matemática real para provar coisas sobre nossos programas (esses seriam os teoremas da adequação e muitos outros). A unidade acima é ruim porque não possui teoremas úteis.

  2. Teoremas que dizem que seus tipos são simples . Um básico seria que é decidível se uma determinada expressão tem um determinado tipo. Outro recurso de simplicidade é fornecer um algoritmo para inferir um tipo. Outros teoremas sobre simplicidade seriam: que uma expressão tem no máximo um tipo ou que uma expressão tem um tipo principal (isto é, o "melhor" entre todos os tipos que possui).

É difícil ser mais específico porque os tipos são um mecanismo muito geral. Mas espero que você veja o que deve filmar. Como a maioria dos aspectos do design da linguagem de programação, não há medida absoluta de sucesso. Em vez disso, existe um espaço de possibilidades de design, e o importante é entender onde você está ou deseja estar no espaço.

Andrej Bauer
fonte
Obrigado por essa resposta detalhada! No entanto, ainda não tenho certeza da resposta à minha pergunta. Como um exemplo concreto, vamos usar C - uma linguagem de tipo estaticamente com um sistema de tipos bastante simples. Se eu escrevesse um typechecker para C, como eu provaria que meu typechecker está "correto"? Como essa resposta muda, se eu escrevi um verificador de tipos para Haskell, digamos HM? Como eu provaria agora "correção"?
Vivek Ghaisas
11
TeATeA
Eu recomendaria fazer 2. e 3. como uma combinação. Além disso, dê uma olhada no CompCert .
Andrej Bauer
11
TeAeAe
AAe
5

Existem algumas coisas diferentes que você poderia dizer com "provar que meu datilógrafo funciona". O que, suponho, faz parte do que sua pergunta está fazendo;)

Metade desta questão está provando que sua teoria de tipos é boa o suficiente para provar quaisquer propriedades sobre a linguagem. A resposta de Andrej aborda muito bem essa área. A outra metade da questão é - supondo que o idioma e seu sistema de tipos já estejam corrigidos - como você pode provar que seu verificador de tipos específico realmente implementa o sistema de tipos corretamente? Existem duas perspectivas principais que posso ver aqui.

Uma é: como podemos confiar que alguma implementação específica corresponde à sua especificação? Dependendo do grau de garantia que você deseja, você pode ficar satisfeito com uma grande suíte de testes ou com algum tipo de verificação formal ou, mais provavelmente, com uma mistura de ambos . O lado positivo dessa perspectiva é que ela realmente destaca a importância de estabelecer limites nas reivindicações que você está fazendo: o que exatamente "correto" significa? qual parte do código é verificada versus qual parte é o TCB correto assumido? etc. A desvantagem é que pensar demais sobre isso leva a um buraco de coelho filosófico - bem, "desvantagem" se você não gosta desses buracos de coelho.

A segunda perspectiva é uma abordagem mais matemática da correção. Ao lidar com linguagens em matemática, geralmente criamos "modelos" para nossas "teorias" (ou vice-versa) e tentamos provar: (a) tudo o que podemos fazer na teoria que podemos fazer no modelo; e (b) tudo o que podemos fazer no modelo que podemos fazer na teoria. (Estes são Solidez e Completudeteoremas. Qual deles depende se você "partiu" da teoria sintática ou do modelo semântico.) Com essa mentalidade, podemos pensar na sua implementação de verificação de tipo como sendo um modelo específico para a teoria de tipos em questão. Portanto, você deseja provar essa correspondência bidirecional entre o que sua implementação pode fazer e o que a teoria diz que você deve fazer. A vantagem dessa perspectiva é que ela realmente se concentra em saber se você cobriu todos os casos principais, se sua implementação está completa no sentido de não deixar de fora nenhum programa que deve aceitar como seguro para o tipo e se sua implementação é sólida. a sensação de não deixar entrar nenhum programa que deveria rejeitar como incorreto. A desvantagem é que sua prova de correspondência provavelmente será bastante separada da própria implementação,

wren romano
fonte
Não tenho certeza se posso concordar com "a vantagem dessa perspectiva é que ela realmente se concentra em saber se você cobriu todos os casos de canto", especialmente se o modelo é apenas sólido, mas não completo. Eu proporia uma perspectiva diferente: passar por um modelo é uma técnica de prova contingente que você usa por vários motivos, por exemplo, porque o modelo é mais simples. Não há nada filosoficamente mais digno em passar por um modelo - em última análise, você quer saber sobre o executável real e seu comportamento.
Martin Berger
Eu pensei que "modelo" e "teoria" fossem entendidos em um sentido amplo, e wren estava apenas enfatizando a importância de tentar estabelecer uma correspondência bidirecional por meio de um "teorema da solidez + completude". (Eu também acho que isso é importante e fiz um comentário no post de Andrej.) É verdade que, em algumas situações, apenas seremos capazes de provar um teorema da solidez (ou um teorema da completude, dependendo da sua perspectiva), mas com ambas as direções em mente é uma restrição metodológica útil.
Noam Zeilberger
11
@NoamZeilberger "A questão é", disse Martin, "se você pode fazer com que as palavras signifiquem tantas coisas diferentes".
Martin Berger
Quando aprendi sobre sistemas de digitação e semântica da linguagem de programação, descobri que modelos são apenas técnicas de prova sobre semântica operacional, e não fins em si mesmos, subliminarmente liberadores.
Martin Berger
11
Relacionar diferentes modelos através da solidez e completude é uma metodologia científica importante para a transferência de insights.
Martin Berger