Esse pode ser um tipo filosófico de pergunta, mas acredito que haja uma resposta objetiva.
Se você ler o artigo da Wikipedia sobre Haskell, poderá encontrar o seguinte:
A linguagem está enraizada nas observações de Haskell Curry e seus descendentes intelectuais, de que "uma prova é um programa; a fórmula que prova é um tipo para o programa"
Agora, o que estou perguntando é: isso realmente não se aplica a praticamente todas as linguagens de programação? Que recurso (ou conjunto de recursos) do Haskell o torna compatível com esta declaração? Em outras palavras, quais são as maneiras visíveis em que essa afirmação afetou o design do idioma?
Respostas:
O conceito essencial se aplica universalmente de alguma maneira, sim, mas raramente de uma maneira útil.
Para começar, da perspectiva da teoria dos tipos, isso pressupõe que as linguagens "dinâmicas" sejam melhor consideradas como tendo um único tipo, que contém (entre outras coisas) metadados sobre a natureza do valor que o programador vê, incluindo o que essas linguagens dinâmicas chamariam um "tipo" em si (que não é a mesma coisa, conceitualmente). É provável que essas provas não sejam interessantes, portanto esse conceito é principalmente relevante para idiomas com sistemas de tipo estático.
Além disso, muitos idiomas que supostamente possuem um "sistema de tipo estático" devem ser considerados dinâmicos na prática, nesse contexto, porque permitem inspeção e conversão de tipos em tempo de execução. Em particular, isso significa qualquer idioma com suporte interno padrão para "reflexão" ou algo semelhante. C #, por exemplo.
Haskell é incomum na quantidade de informações que espera que um tipo forneça - em particular, as funções não podem depender de nenhum valor além dos especificados como argumentos. Em uma linguagem com variáveis globais mutáveis, por outro lado, qualquer função pode (potencialmente) inspecionar esses valores e alterar o comportamento de acordo. Assim, uma função Haskell com o tipo
A -> B
pode ser considerado como uma prova programa miniatura queA
implicaB
; uma função equivalente em muitas outras línguas apenas nos diria queA
e qualquer estado global que esteja no escopo combinado implicaB
.Observe que, embora o Haskell tenha suporte para coisas como reflexão e tipos dinâmicos, o uso desses recursos deve ser indicado na assinatura de tipo de uma função; da mesma forma para o uso do estado global. Nenhum dos dois está disponível por padrão.
Não são maneiras de quebrar as coisas em Haskell, bem como, por exemplo, permitindo exceções de tempo de execução, ou usando operações primitivas não-padrão fornecidos pelo compilador, mas aqueles vêm com uma forte expectativa de que eles só serão utilizadas com plena compreensão de maneiras que ganharam' t danifica o significado do código externo. Em teoria, o mesmo poderia ser dito de outras línguas, mas, na prática, com a maioria das outras línguas, é mais difícil realizar as coisas sem "trapacear" e menos desaprovado em "trapacear". E, claro, nas verdadeiras linguagens "dinâmicas", a coisa toda permanece irrelevante.
O conceito pode ser levado muito mais longe do que em Haskell também.
fonte
Você está certo de que a correspondência Curry-Howard é uma coisa muito geral. Vale a pena se familiarizar um pouco com a história: http://en.wikipedia.org/wiki/Curry-Howard_correspondence
Você notará que, conforme formulada originalmente, essa correspondência se aplicava particularmente à lógica intuicionista, por um lado, e ao cálculo lambda simplesmente digitado (STLC), por outro.
Haskell clássico - versões de 98 ou mesmo anteriores, foi muito próximo do STLC, e na maioria das vezes houve uma tradução direta e muito simples entre qualquer expressão dada em Haskell e um termo correspondente no STLC (estendido com recursão e alguns tipos primitivos). Então, isso tornou Curry-Howard muito explícito. Hoje, graças às extensões, essa tradução é um negócio um pouco mais complicado.
Então, em certo sentido, a questão é por que Haskell "desugars" no STLC de maneira tão direta. Duas coisas vem a mente:
Há também uma maneira importante pela qual Haskell, como a maioria dos idiomas, falha em relação à aplicação direta da correspondência de Curry-Howard. Haskell, como uma linguagem completa, contém a possibilidade de recursão ilimitada e, portanto, de não terminação. O STLC não possui um operador de ponto de fixação, não está completo e está fortemente normalizando - o que significa que nenhuma redução de um termo no STLC falhará ao finalizar. A possibilidade de recursão significa que se pode "trapacear" Curry-Howard. Por exemplo,
let x = x in x
tem o tipoforall a. a
- ou seja, como nunca retorna, posso fingir que me dá qualquer coisa! Como sempre podemos fazer isso em Haskell, isso significa que não podemos "acreditar" totalmente em qualquer prova correspondente a um programa Haskell, a menos que tenhamos uma prova separada de que o próprio programa está sendo encerrado.A linhagem da programação funcional anterior a Haskell (principalmente a família ML) foi o resultado de uma pesquisa de CS focada em criar linguagens sobre as quais você poderia facilmente provar coisas (entre outras coisas), pesquisar muito ciente e decorrente do CH para começar. Por outro lado, Haskell serviu de linguagem de host e de inspiração para vários assistentes de prova em desenvolvimento, como Agda e Epigram, que estão enraizados em desenvolvimentos na teoria de tipos muito relacionados à linhagem de CH.
fonte
A -> B
, dada aA
, produzirá umB
ou nada. Ele nunca produzirá aeC
qual valor do tipoB
fornece, ou se diverge, ainda depende exclusivamente doA
fornecido.Void
, não? A preguiça torna cada vez mais complicado. Eu diria que uma funçãoA -> B
sempre produz um valor do tipoB
, mas esse valor pode ter menos informações do que se esperaria.Para uma aproximação de primeira ordem, a maioria das outras linguagens (fracamente e / ou uni-tipadas) não suporta o rigoroso delineamento no nível de idioma entre um
e a relação estrita entre os dois. Na verdade, as melhores garantias fornecidas por outros idiomas são
Observe que, por tipo , nos referimos a uma proposição e, portanto, a algo que descreve muito mais informações do que apenas int ou bool . Em Haskell, existe uma cultura permeante de uma função que é afetada apenas por seus argumentos - sem exceções *.
Para ser um pouco mais rigoroso, a idéia geral é que, ao aplicar uma abordagem intuicionista rígida para (quase) todas as construções de programa (isto é, só podemos provar o que podemos construir) e limitar o conjunto de construções primitivas de tal forma maneira que nós temos
As construções de Haskell tendem a prestar-se muito bem ao raciocínio sobre seu comportamento. Se pudermos construir uma prova (leia-se: function) provando que isso
A
implicaB
, isso tem propriedades muito úteis:A
, podemos construir umB
)A
, e nada mais.permitindo assim raciocinar sobre invariantes locais / globais de maneira eficaz. Voltar à pergunta original; Os recursos de linguagem de Haskell que melhor facilitam essa mentalidade são:
Nenhuma delas é única para Haskell (muitas dessas idéias são incrivelmente antigas). No entanto, quando combinados com um rico conjunto de abstrações nas bibliotecas padrão (geralmente encontradas nas classes de tipos), com vários níveis de sintaxe e um compromisso estrito com a pureza no design do programa, terminamos com uma linguagem que de alguma forma consegue ser prático o suficiente para aplicações do mundo real , mas ao mesmo tempo é mais fácil argumentar sobre as línguas mais tradicionais.
Essa pergunta merece uma resposta suficientemente profunda, e eu não poderia fazer justiça nesse contexto. Eu sugiro ler mais na wikipedia / na literatura:
* NB: Estou ignorando / ignorando alguns dos aspectos mais complicados das impurezas de Haskell (exceções, não terminação etc.) que apenas complexariam o argumento.
fonte
Qual recurso? O sistema de tipos (sendo estático, puro, polimórfico). Um bom ponto de partida são os "Teoremas de graça" de Wadler. Efeito perceptível no design do idioma? O tipo de E / S, tipo classes.
fonte
A hierarquia Kleene nos mostra que provas não são programas.
A primeira relação recursiva é:
As primeiras relações recursivamente enumeráveis são:
Portanto, um programa é um teorema e a iteração que existe na qual o programa pára é como a prova que existe que prova o teorema.
Quando um programa é produzido corretamente a partir de uma especificação, devemos ser capazes de provar que satisfaz a especificação e, se podemos provar que um programa satisfaz uma especificação, é a síntese correta do programa. Portanto, executamos a síntese do programa se provarmos que o programa atende à especificação. O teorema de que o programa satisfaz a especificação é o programa em que o teorema se refere ao programa que é sintetizado.
As falsas conclusões de Martin Lof nunca produziram nenhum programa de computador e é incrível que as pessoas acreditem que seja uma metodologia de síntese de programa. Nunca foram dados exemplos completos de um programa sendo sintetizado. Uma especificação como "insira um tipo e produza um programa desse tipo" não é uma função. Existem vários programas desse tipo e escolher um aleatoriamente não é uma função recursiva ou mesmo uma função. É apenas uma tentativa boba de mostrar a síntese do programa com um programa bobo que não representa um programa de computador real calculando uma função recursiva.
fonte
doesn't this really apply to pretty much all the programming languages?
" Esta resposta afirma / mostra que essa suposição é inválida, portanto não faz sentido abordar o restante das perguntas que são baseadas em uma premissa falha .