Por que não estamos pesquisando mais sobre garantias de tempo de compilação?

12

Adoro tudo o que é tempo de compilação e adoro a idéia de que, depois que você compila um programa, muitas garantias são feitas sobre sua execução. De um modo geral, um sistema de tipo estático (Haskell, C ++, ...) parece oferecer garantias mais fortes em tempo de compilação do que qualquer sistema de tipo dinâmico.

Pelo que entendi, Ada vai ainda mais longe no que diz respeito à verificação do tempo de compilação e é capaz de detectar uma gama maior de erros antes da execução. Também é considerado bastante seguro, já que, em algum momento, foi escolhido para campos delicados (quando erros de programação podem custar vidas humanas).

Agora, eu me pergunto: se garantias estáticas mais fortes levam a códigos mais documentados e seguros, por que não estamos pesquisando mais nessa direção?

Um exemplo de algo que parece estar faltando seria uma linguagem que, em vez de definir um inttipo genérico que possui um intervalo determinado pelo número de bits da arquitetura subjacente, poderia ter intervalos (no exemplo a seguir Int [a..b]descreve um tipo inteiro entre aeb incluídos):

a : Int [1..24]
b : Int [1..12]
a + b : Int [2..36]
a - b : Int [-11..23]
b - a : Int [-23..11]

ou (tirando isso de Ada):

a : Int [mod 24]
b : Int [mod 24]
a + b : Int [mod 24]

Esse idioma selecionaria o melhor tipo subjacente para o intervalo e executaria a verificação do tempo de compilação nas expressões. Então, por exemplo, dado:

a : Int [-3..24]
b : Int [3..10]

então:

a / b

nunca será definido.

Este é apenas um exemplo, mas sinto que há muito mais que podemos aplicar em tempo de compilação. Então, por que parece haver tão pouca pesquisa sobre isso? Quais são os termos técnicos que descrevem essa ideia (para que eu possa encontrar mais informações sobre este tópico)? Quais são os limites?

Sapato
fonte
2
O Pascal possui tipos de subintervalos inteiros (por exemplo, década de 1960), mas infelizmente a maioria das implementações apenas os verifica em tempo de execução (int (-1..4) é uma atribuição compatível com int (100..200) em tempo de compilação). Os benefícios são limitados e a programação baseada em contratos estende a idéia em uma direção melhor (Eiffel, por exemplo). Idiomas como o C # tentam obter alguns desses benefícios com atributos, eu não os usei para não ter certeza da utilidade deles na prática.
1
@ Nota: Os atributos em C # são apenas classes de metadados, portanto qualquer validação de dados ocorreria em tempo de execução.
Robert Harvey
8
Como você sabe que há pouca pesquisa sobre isso? Tente pesquisar no Google dependent typeou refinement type.
Phil
3
Concordo que a premissa parece falho; este é certamente um campo de pesquisa ativo. O trabalho nunca é feito . Portanto, não vejo bem como essa pergunta pode ser respondida.
Raphael
1
@ Robert Harvey: O fato de o ADA fornecer mais garantias não significa que o compilador irá capturar todos os erros, apenas os tornará menos prováveis.
Giorgio

Respostas:

11

Não estou em posição de dizer o quanto mais investigação deve ser feita sobre o tema, mas posso dizer-lhe que não é pesquisa que está sendo feito, por exemplo, o Verisoft XT programa financiado pelo governo alemão.

Os conceitos que eu acho que você está procurando são chamados verificação formal e programação baseada em contrato , onde o último é uma maneira amigável de programador de fazer o primeiro. Na programação baseada em contrato, você primeiro escreve seu código normalmente e depois insere os chamados contratos no código. Uma linguagem facilmente utilizável baseada nesse paradigma é a Spec # da Microsoft Research, e a extensão Code Contracts funcionalmente semelhante, mas um pouco menos bonita para C #, que você pode experimentar on-line (elas também têm ferramentas semelhantes para outros idiomas, consulte rise4fun ) O "tipo int com intervalo" que você mencionou seria refletido por dois contratos em uma função:

Contract.Requires(-3 <= a && a <= 24);
Contract.Requires( 3 <= b && b <= 10);

Se você quiser chamar essa função, precisará usar parâmetros garantidos para atender a esses critérios ou obter um erro de tempo de compilação. Os contratos acima são muito simples, você pode inserir quase qualquer suposição ou requisito sobre variáveis ​​ou exceções e sua relação em que você possa pensar, e o compilador verificará se todos os requisitos são cobertos por uma suposição ou algo que possa ser garantido, ou seja, derivado das suposições. É por isso que de onde o nome deriva: O chamado requer requisitos , o chamador garante que eles sejam atendidos - como em um contrato comercial.

P(x1,x2,...,xn)nPé usado. Do lado do CS, essas duas são as partes críticas do processo - a geração de condições de verificação é complicada e o SMT é um problema completo do NP ou indecidível, dependendo das teorias consideradas. Existe até uma competição para solucionadores de SMT, então certamente há alguma pesquisa sobre isso. Além disso, existem abordagens alternativas ao uso do SMT para verificação formal, como enumeração de espaço de estado, verificação de modelo simbólico, verificação de modelo limitado e muito mais que também estão sendo pesquisadas, embora o SMT seja, atualmente, a abordagem mais "moderna".

Em relação aos limites da ideia geral:

  • Como declarado anteriormente, provar a correção do programa é um problema computacionalmente difícil, portanto, é possível que a verificação do tempo de compilação de um programa com contratos (ou outra forma de especificação) demore muito ou seja impossível. A aplicação de heurísticas que funcionam bem na maioria das vezes é o melhor que se pode fazer a respeito.
  • Quanto mais você especificar sobre o seu programa, maior será a probabilidade de ter erros na própria especificação . Isso pode levar a falsos positivos (a verificação do tempo de compilação falha, mesmo que tudo esteja livre de erros) ou a falsa impressão de segurança, mesmo que seu programa ainda tenha erros.
  • Escrever contratos ou especificações é realmente um trabalho tedioso e a maioria dos programadores tem preguiça de fazê-lo. Tente escrever um programa C # com contratos de código em qualquer lugar, depois de um tempo você pensará "vamos lá, isso é realmente necessário?". É por isso que a verificação formal geralmente é usada apenas para projetos críticos de hardware e sistemas críticos de segurança, como aviões de controle de software ou automóveis.

Uma última coisa que vale a pena mencionar que não se encaixa perfeitamente na explicação acima é um campo chamado "Teoria da complexidade implícita", por exemplo, este artigo . O objetivo é caracterizar as linguagens de programação nas quais cada programa que você pode escrever se enquadra em uma classe de complexidade específica, por exemplo P. Nesse idioma, cada programa que você escreve é ​​automaticamente "garantido" como sendo de tempo de execução polinomial, que pode ser "verificado" em tempo de compilação, simplesmente compilando o programa. Entretanto, não conheço nenhum resultado praticamente utilizável desta pesquisa, mas também estou longe de ser um especialista.

Stefan Lutz
fonte
Não deveria ser possível gerar tipos ou contratos dependentes a partir de uma combinação de exemplo (s) de teste e código não digitado, dada uma certa "estratégia" que você pode escolher dependendo do seu projeto?
aoeu256 08/09/19