Recentemente, concluí um curso universitário que apresentava Haskell e Agda (uma linguagem de programação funcional com tipo dependente) e queria saber se seria possível substituir o cálculo lambda nestes pela lógica combinatória. Com Haskell, isso parece possível usando os combinadores S e K, tornando-o livre de pontos. Fiquei me perguntando qual seria o equivalente para Agda. Ou seja, pode-se fazer uma linguagem de programação funcional com tipo dependente equivalente a Agda sem usar nenhuma variável?
Além disso, é possível substituir de alguma forma a quantificação por combinadores? Não sei se isso é uma coincidência, mas a quantificação universal, por exemplo, faz uma assinatura de tipo parecer uma expressão lambda. Existe uma maneira de remover a quantificação universal de uma assinatura de tipo sem alterar seu significado? Por exemplo, em:
forall a : Int -> a < 0 -> a + a < a
A mesma coisa pode ser expressa sem usar um forall?
Respostas:
Então, pensei um pouco mais e fiz alguns progressos. Aqui está uma primeira tentativa de codificar o sistema deliciosamente simples (mas inconsistente) de Martin-Löf
Set : Set
em um estilo combinatório. Não é uma boa maneira de terminar, mas é o lugar mais fácil para começar. A sintaxe dessa teoria de tipo é apenas lambda-cálculo com anotações de tipo, tipos Pi e um conjunto de universo.A Teoria do Tipo de Alvo
Para fins de integridade, apresentarei as regras. A validade de contexto apenas diz que você pode construir contextos a partir do vazio, juntando novas variáveis que habitam
Set
s.E agora podemos dizer como sintetizar tipos para termos em qualquer contexto dado, e como alterar o tipo de algo até o comportamento computacional dos termos que ele contém.
Em uma pequena variação do original, tornei lambda o único operador de ligação, portanto, o segundo argumento de Pi deve ser uma função que calcula a maneira como o tipo de retorno depende da entrada. Por convenção (por exemplo, em Agda, mas infelizmente não em Haskell), o escopo de lambda se estende para a direita tanto quanto possível, então você pode frequentemente deixar as abstrações sem colchetes quando elas são o último argumento de um operador de ordem superior: você pode ver que eu fiz isso com Pi. Seu tipo Agda
(x : S) -> T
se tornaPi S \ x:S -> T
.( Digressão . Anotações de tipo em lambda são necessárias se você quiser ser capaz de sintetizar o tipo de abstrações. Se você alternar para verificação de tipo como seu modus operandi, ainda precisará de anotações para verificar um beta-redex como
(\ x -> t) s
, porque você não tem como para adivinhar os tipos das partes do todo. Aconselho os designers modernos a verificar os tipos e excluir beta-redexes da própria sintaxe.)( Digressão . Este sistema é inconsistente, pois
Set:Set
permite a codificação de uma variedade de "paradoxos mentirosos". Quando Martin-Löf propôs esta teoria, Girard enviou-lhe uma codificação em seu próprio Sistema U inconsistente. O paradoxo subsequente devido a Hurkens é o a construção tóxica mais limpa que conhecemos.)Sintaxe e normalização do combinador
De qualquer forma, temos dois símbolos extras, Pi e Set, então talvez possamos fazer uma tradução combinatória com S, K e dois símbolos extras: eu escolhi U para o universo e P para o produto.
Agora podemos definir a sintaxe combinatória não tipada (com variáveis livres):
Observe que incluí os meios para incluir variáveis livres representadas por tipo
a
nesta sintaxe. Além de ser um reflexo de minha parte (toda sintaxe digna desse nome é uma mônada livre comreturn
variáveis de incorporação e>>=
substituição de desempenho), será útil representar estágios intermediários no processo de conversão de termos com ligação à sua forma combinatória.Aqui está a normalização:
(Um exercício para o leitor é definir um tipo exatamente para as formas normais e aprimorar os tipos dessas operações.)
Representando a Teoria do Tipo
Agora podemos definir uma sintaxe para nossa teoria de tipos.
Eu uso uma representação de índice de Bruijn à maneira de Bellegarde e Hook (popularizada por Bird e Paterson). O tipo
Su a
tem mais um elemento quea
, e nós o usamos como o tipo de variáveis livres em um fichário, comZe
como a variável recém-ligada eSu x
sendo a representação deslocada da antiga variável livrex
.Traduzindo Termos para Combinadores
E com isso feito, adquirimos a tradução usual, baseada na abstração de colchetes .
Digitando os Combinadores
A tradução mostra a maneira como usamos os combinadores, o que nos dá uma boa pista sobre quais deveriam ser seus tipos.
U
eP
são apenas construtores de conjuntos, então, escrevendo tipos não traduzidos e permitindo "notação Agda" para Pi, devemos terO
K
combinador é usado para elevar um valor de algum tipoA
a uma função constante sobre algum outro tipoG
.O
S
combinador é usado para levantar aplicações sobre um tipo, do qual todas as partes podem depender.Se você observar o tipo de
S
, verá que ele afirma exatamente a regra de aplicação contextualizada da teoria de tipo, de modo que é o que o torna adequado para refletir a construção do aplicativo. Esse é o seu trabalho!Então, temos aplicação apenas para coisas fechadas
Mas há um obstáculo. Eu escrevi os tipos de combinadores na teoria dos tipos comuns, não na teoria dos tipos combinatórios. Felizmente, tenho uma máquina que fará a tradução.
Um sistema de tipo combinatório
Então aí está, em toda a sua glória ilegível: uma apresentação combinatória de
Set:Set
!Ainda há um pequeno problema. A sintaxe do sistema não oferece nenhuma maneira de adivinhar os parâmetros
G
,A
eB
paraS
e de forma semelhanteK
, apenas a partir dos termos. Da mesma forma, podemos verificar as derivações de tipagem algoritmicamente, mas não podemos simplesmente verificar os termos do combinador como poderíamos com o sistema original. O que pode funcionar é exigir que a entrada para o verificador de tipo tenha anotações de tipo sobre os usos de S e K, registrando efetivamente a derivação. Mas essa é outra lata de vermes ...Este é um bom lugar para parar, se você estiver interessado em começar. O resto é coisa de "bastidores".
Gerando os Tipos de Combinadores
Eu gerei esses tipos combinatórios usando a tradução de abstração de colchetes dos termos relevantes da teoria de tipo. Para mostrar como fiz isso, e tornar este post não totalmente inútil, deixe-me oferecer meu equipamento.
Posso escrever os tipos de combinadores, totalmente abstraídos de seus parâmetros, como segue. Eu uso minha
pil
função prática , que combina Pi e lambda para evitar a repetição do tipo de domínio e, de forma bastante útil, me permite usar o espaço de funções de Haskell para vincular variáveis. Talvez você quase possa ler o seguinte!Com eles definidos, extraí os subtermos abertos relevantes e os executei na tradução.
A de Bruijn Encoding Toolkit
Veja como construir
pil
. Em primeiro lugar, defino uma classe deFin
conjuntos de itens, usados para variáveis. Cada um desses conjuntos tem umemb
edding que preserva o construtor no conjunto acima, além de um novotop
elemento, e você pode diferenciá-los: aembd
função informa se um valor está na imagem deemb
.Podemos, é claro, instanciar
Fin
paraZe
eSuc
Agora posso definir menos ou igual, com uma operação de enfraquecimento .
A
wk
função deve incorporar os elementos dex
como os maiores elementos dey
, de modo que as coisas extras emy
sejam menores e, portanto, em termos de índice de de Bruijn, limitados mais localmente.E uma vez que você tenha resolvido isso, um pouco de malandragem faz o resto.
A função de ordem superior não fornece apenas um termo que representa a variável, mas também uma coisa sobrecarregada que se torna a representação correta da variável em qualquer escopo onde a variável esteja visível. Ou seja, o fato de que me dou ao trabalho de distinguir os diferentes escopos por tipo dá ao inspetor de tipos Haskell informações suficientes para calcular o deslocamento necessário para a tradução para a representação de Bruijn. Por que ter um cachorro e latir sozinho?
fonte
F
combinador?F
age de forma diferente dependendo de seu primeiro argumento: SeA
é um átomo,M
eN
são termos ePQ
é um composto, entãoFAMN -> M
eF(PQ)MN -> NPQ
. Isso não pode ser representado emSK(I)
cálculo, masK
pode ser representado comoFF
. É possível estender seu ponto MLTT grátis com isso?λ (A : Set) → λ (a : A) → a
de tipo(A : Set) → (a : A) → A
é traduzido paraS(S(KS)(KK))(KK)
, que não pode ser usado em um tipo em que o tipo do segundo argumento depende do primeiro argumento.Acho que a "Abstração de colchetes" também funciona para tipos dependentes em algumas circunstâncias. Na seção 5 do seguinte artigo, você encontrará alguns tipos K e S:
Coincidências ultrajantes, mas significativas
Sintaxe e avaliação de tipo seguro dependente
Conor McBride, University of Strathclyde, 2010
Converter uma expressão lambda em uma expressão combinatória corresponde aproximadamente a converter uma prova de dedução natural em uma prova de estilo de Hilbert.
fonte