cálcio com reflexão


23

Estou procurando um cálculo simples que suporte o raciocínio sobre reflexão , a saber, a introspecção e manipulação de programas em execução.

Existe uma extensão de cálcio não tipada que permita converter termos λ em um formato que possa ser sintaticamente manipulado e depois avaliado posteriormente?λλ

Imagino que o cálculo tenha dois termos adicionais principais:

  • : pega ve produz uma representação de v corrigível para manipulação sintática.reflect vvv
  • : pega uma representação sintática de um termo e a avalia.eval v

Para apoiar a reflexão, é necessária uma representação sintática dos termos. Seria algo como:

  • será portanto representada por um termo ( G A M R ( e ) ) , em que R ( e ) é a versão reflectida de e ,λx.e(LAM R(e))R(e)e
  • seria representado como termo ( A P P R ( e ) R ( e ' ) ) , ee e(APP R(e) R(e))
  • seria representado como ( V A R x ) .x(VAR x)

Com essa representação, a correspondência de padrões pode ser usada para manipular termos.

Mas nos deparamos com um problema. e e v um l necessidade de ser codificada como termos, como o faz a correspondência de padrões. Lidando com isto parece ser simples, adicionando R E F G E C T , E V A G e H A T C H , mas será necessário adicionar outros termos para suportar a manipulação destes?reflectevalREFLECTEVALMATCH

Existem opções de design que precisam ser feitas. Qual deve ser o função aludido acima fazer com o corpo de r e f l e c t e e v a l ? Should R ( - ) transformar o corpo ou não?R()reflectevalR()

Como não estou muito interessado em estudar a própria reflexão - o cálculo serviria de veículo para outras pesquisas - não quero reinventar a roda.

Existe algum cálculo existente que corresponda ao que acabei de descrever?

Até onde eu sei, cálculos como o MetaML, sugerido em um comentário, percorrem um longo caminho, mas eles não incluem a capacidade de combinar padrões e desconstruir fragmentos de código que já foram criados.

Uma coisa que eu gostaria de poder fazer é o seguinte:

  • let x=λy.y in reflect x(LAM (VAR y) (VAR y))

E, em seguida, execute a correspondência de padrões no resultado para criar uma expressão completamente diferente.

Certamente, essa não é uma extensão conservadora do cálcio e a metateoria provavelmente é feia, mas esse é o ponto da minha aplicação. Eu quero separar as λ- distrações.λλ


O MetaML é uma linguagem reflexiva digitada, com o operador de bracketing executando seu REFLECT e o bracketing EVAL. A digitação é básica, mas você pode ver o fragmento herdado do modal S4 em um trabalho como este artigo que pode ajudá-lo.
Ex0du5

@ ex0du5: Obrigado, mas isso não vai longe o suficiente, tanto quanto eu posso dizer. Claro, eu posso criar código em várias fases, mas não consigo separar os termos. (Vou ler com mais atenção, para ver se eu perdi alguma coisa.)
Dave Clarke

Esquema (sem a mutabilidade e outras complicações)?
Gilles 'SO- stop be evil'

@ Gilles: Scheme é uma linguagem de programação, não um cálculo. Além disso, acho que não pode fazer o que quero.
Dave Clarke

@DaveClarke Uma linguagem de programação é um cálculo com muitas verrugas. Um núcleo do esquema parece adequado à primeira vista, mas eu não dei a seus requisitos pensamentos suficientes para ter certeza. O que você acha que não funcionaria? (Entre para conversar, se quiser.)
Gilles 'SO- stop be evil'

Respostas:


15

Jean Louis Krivine introduziu um cálculo abstrato que estende a "Máquina Krivine" de uma maneira não trivial (observe que a máquina Krivine já suporta a instrução call / cc do lisp):

Ele introduz um operador de "quote" no presente artigo definido da seguinte maneira: se é uma λ -termo, nota n & Phi; a imagem de φ por alguns bijection π : Ganhe muitos N a partir de termos lambda para números naturais. Nota ¯ n o numeral igreja que corresponde a n N . Krivine define o operador χ pela regra de avaliação: χ ϕ ϕ ¯ n ϕϕλnϕϕπ:ΛNn¯nNχ

χ ϕϕ nϕ¯
Acredito que a magia Kleene mostrará que isso é suficiente para fazer o que você deseja: ie definir operadores de cotação e avaliação, se for computável.π

Observe que Krivine é notoriamente desafiador de ler (por favor, não fique bravo se você ler isso, Jean-Louis!), E alguns pesquisadores fizeram o ato de caridade de tentar extrair o conteúdo técnico de uma maneira mais legível. Você pode tentar dar uma olhada nessas anotações de Christophe Raffali.

Espero que isto ajude!


Ocorre-me que há outra referência que pode ser relevante para seus interesses: o Pure Pattern Calculus, de Jay e Kesner, formaliza uma variante do cálcio que estende a abstração simples sobre uma variável para uma abstração sobre um padrão que representa um cálculo padrão. em si. Isso é fenomenalmente expressivo e, em particular, permite desconstruir um aplicativo em si: se não me engano, o termo:λ

(λ(x y).x)((λx.x x) (λy.y))

λx.x x


Gostaria de aprovar esta resposta aparentemente razoável, mas não tenho ideia se ela começa a responder à pergunta.
Raphael

@ Rafael leia os artigos e descubra :) Na verdade, essa é apenas uma resposta parcial: os artigos formalizam de fato um recurso importante do lisp não encontrado no cálculo lambda: o operador QUOTE. Porém, não há um estudo metateórico extenso, eles apenas o introduzem como um meio de expressar um tipo de computação estranha e não transparente, a fim de realizar axiomas complicados da teoria dos conjuntos.
C23

1
(λx.x x) (λy.y)λy.y(x y)

1
quotereflectquote

8

Fazer isso é muito difícil, se não impossível, sem abrir mão da confluência. Ou seja, suspeito que você esteja certo sobre uma meta-teoria cabeluda. Por outro lado, é possível projetar um cálculo combinador que possa expressar todas as funções computáveis ​​do turing e que tenha capacidade total para inspecionar seus termos: veja Jay e Give-Wilson .

Porém, acredito que ter essa capacidade faz algumas coisas ruins à sua teoria equacional. Em particular, você tenderá a provar apenas que dois valores são iguais se o for igual a equivalências alfa.

Ainda não li o artigo sobre Krivine vinculado a ele, mas devo observar que, na lógica clássica, você basicamente tem apenas duas coisas: verdadeiro e falso. Tudo é equivalente a um desses. Ou seja, você tende a ter uma teoria equacional em colapso de qualquer maneira.


1
Observe que o cálculo de Krivine não é um cálculo de proposições, mas sim de realizadores para elas, que possuem uma teoria equacional altamente não trivial.
Cody

5

Na teoria das linguagens de programação, o recurso sobre o qual você fala é geralmente chamado de "citação". Por exemplo, John Longley escreveu sobre isso em alguns de seus trabalhos, veja este artigo .

Se você estiver apenas após considerações teóricas (em oposição a uma implementação realmente útil), poderá simplificar as coisas afirmando que quote(ou reflectcomo você o chama) mapeia para o tipo de números inteiros nat, retornando um código Gödel de seu argumento. Você pode decompor o número da mesma maneira que faria com uma árvore de sintaxe abstrata. Além disso, você não precisa, evalporque isso pode ser implementado no idioma - é essencialmente um intérprete para o idioma.

nmφn(m)φnnλquote

Se você me disser o que está procurando, talvez eu possa lhe dar referências mais específicas.

A propósito, aqui está um problema em aberto:

λquoteξ

ξ

e1e2λx.e1λx.e2
λλquotequote
e1e2quotee1quotee2,
quote((λx.x)y)quotey.
quoteλ

βquoteλξ

ξβ

O artigo a seguir mostra alguns problemas com a equação (ξ): O Cálculo Lambda é algébrico, Peter Selinger. Interessante, algo novo que eu não sabia! Legal.
Números transfinitos

4

Aqui está uma resposta alternativa, em vez de usar minha abordagem nominal, que ainda é experimental, há uma abordagem mais estabelecida que remonta ao artigo:

LEAP: Uma linguagem com avaliação e polimorfismo
Frank Pfenning e Peter Lee
https://www.cs.cmu.edu/~fp/papers/tapsoft89.pdf

O artigo começa com:

Isso então nos levou à questão, colocada pela primeira vez por Reynolds, de saber se linguagens fortemente tipadas admitem intérpretes metacirculares. A sabedoria convencional parecia indicar que a resposta era "Não". Nossa resposta é "quase".

Observe que o LEAP é muito mais forte do que o que o OP deseja. Primeiro de tudo, é digitado. E segundo, pede metacircularidade, o que significa, por exemplo, que eval pode executar sua própria definição. No Prolog, você obtém metacircularidade para resolver / 1:

solve(true).
solve((A,B)) :- solve(A), solve(B).
solve(H) :- clause(H,B), solve(B).

Se você adicionar a seguinte cláusula para resolver / 1:

solve(clause(H,B)) :- clause(H,B).

E se você observar, a cláusula / 2 também retorna as cláusulas de resolve / 1. Você pode ligar para resolver (resolver (...)) e ver como a resolução se executa.

Questões de auto-representação ainda estimulam algumas pesquisas, veja, por exemplo:

Auto-representação no Sistema Girards U
Matt Brown, Jens Palsberg
http://compilers.cs.ucla.edu/popl15/popl15-full.pdf


3

O problema é identificado na proximidade de assistentes de prova como Coq e Isabelle / HOL. Sob o acrônimo HOAS . Existem algumas afirmações em torno do λ-Prolog de que, através do novo quantificador such, tais coisas podem ser feitas. Mas ainda não consegui entender essa afirmação. Acho que o principal insight que obtive até agora é que não existe uma abordagem definida, existem algumas abordagens possíveis.

Minha opinião, ainda não concluída , é inspirada em um artigo recente de Paulson sobre a comprovação da incompletude de Gödels. Eu usaria os ligantes no nível do objeto em conexão com alguma estrutura de dados que possui os nomes no nível meta. Basicamente, uma estrutura de dados semelhante, porém distinta, como a do OP, e com a codificação da Igreja, pois estou interessado em tipos dependentes:

datatype Expr = var Name                 /* written as n */
              | app Expr Expr            /* written as s t */
              | abs Name Expr Expr       /* written as λn:s.t */

As expressões no nível meta podem ser distinguidas das expressões no nível do objeto, pois usamos os nomes das variáveis ​​n, m, etc. etc. para denotar nomes. Considerando que usamos os nomes das variáveis ​​x, y, etc. etc. no nível do objeto. A interpretação de um meta termo na lógica do objeto funcionaria da seguinte maneira. Vamos escrever [t] σ para a interpretação do termo nominal t no contexto nominal σ, que deve fornecer um termo de objeto. Teríamos então:

 [n]σ = lookup σ n
 [s t]σ = [s]σ [t]σ
 [λn:s.t]σ = λx:[s]σ.[t]σ,n:x

O item acima definiria o que o OP chama de função EVAL. Pequena diferença para Paulson, σ é apenas uma lista finita e não funcional. Na minha opinião, só seria possível introduzir uma função EVAL e não uma função REFLECT. Como no nível do objeto, você pode ter alguma igualdade para que diferentes expressões lambda sejam iguais. O que você precisa fazer é usar eval para raciocinar, possivelmente também sobre reflexão, se você sentir a necessidade.

Você precisaria ir a extremos como o Prolog, onde nada é expandido, se quiser derrubar a parede entre nominal e não nominal. Mas, como mostra o exemplo do sistema λ-Prolog, no caso de ordem superior, há problemas adicionais atraentes que, por exemplo, só podem ser superados de maneira lógica, através da introdução de novos meios, como um quantificador!!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.