Até onde podem ir as macros LISP?[fechado]
Pergunta
Eu li muito que o LISP pode redefinir a sintaxe rapidamente, provavelmente com macros.Estou curioso para saber até onde isso realmente vai?Você pode redefinir tanto a estrutura da linguagem que ela se torna um compilador para outra linguagem?Por exemplo, você poderia mudar a natureza funcional do LISP para uma sintaxe e semântica mais orientadas a objetos, talvez digamos, tendo uma sintaxe mais próxima de algo como Ruby?
Principalmente, é possível se livrar do inferno dos parênteses usando macros?Aprendi (Emacs-)LISP o suficiente para personalizar o Emacs com meus próprios micro-recursos, mas estou muito curioso para saber até onde as macros podem ir na personalização da linguagem.
Solução
Essa é uma pergunta muito boa.
Acho que tem nuances, mas definitivamente é responsável:
As macros não ficam presas em expressões s.Veja a macro LOOP para uma linguagem muito complexa escrita com palavras-chave (símbolos).Portanto, embora você possa iniciar e terminar o loop com parênteses, dentro dele há sua própria sintaxe.
Exemplo:
(loop for x from 0 below 100
when (even x)
collect x)
Dito isto, a maioria das macros simples usa apenas expressões s.E você ficaria "preso" a usá-los.
Mas as expressões S, como Sergio respondeu, começam a parecer certas.A sintaxe sai do caminho e você começa a codificar na árvore de sintaxe.
Quanto às macros de leitura, sim, você poderia escrever algo assim:
#R{
ruby.code.goes.here
}
Mas você precisaria escrever seu próprio analisador de sintaxe Ruby.
Você também pode imitar algumas das construções Ruby, como blocos, com macros que compilam para as construções Lisp existentes.
#B(some lisp (code goes here))
traduziria para
(lambda () (some lisp (code goes here)))
Ver esta página para saber como fazer isso.
Outras dicas
Sim, você pode redefinir a sintaxe para que o Lisp se torne um compilador.Você faz isso usando "Macros de leitura", que são diferentes das "Macros de compilador" normais nas quais você provavelmente está pensando.
Common Lisp tem o recurso integrado para definir uma nova sintaxe para o leitor e as macros do leitor processarem essa sintaxe.Esse processamento é feito em tempo de leitura (que vem antes do tempo de compilação ou avaliação).Para saber mais sobre como definir macros de leitura no Common Lisp, consulte Common Lisp Hyperspec - você vai querer ler CH.2, "Sintaxe" e CH.23, "Leitor".(Acredito que Scheme tenha a mesma facilidade, mas não estou tão familiarizado com isso - veja o Fontes do esquema para o Linguagem de programação Arc).
Como um exemplo simples, suponhamos que você queira que o Lisp use chaves em vez de parênteses.Isso requer algo como as seguintes definições do leitor:
;; { and } become list delimiters, along with ( and ).
(set-syntax-from-char #\{ #\( )
(defun lcurly-brace-reader (stream inchar) ; this was way too easy to do.
(declare (ignore inchar))
(read-delimited-list #\} stream t))
(set-macro-character #\{ #'lcurly-brace-reader)
(set-macro-character #\} (get-macro-character #\) ))
(set-syntax-from-char #\} #\) )
;; un-lisp -- make parens meaningless
(set-syntax-from-char #\) #\] ) ; ( and ) become normal braces
(set-syntax-from-char #\( #\[ )
Você está dizendo ao Lisp que { é como a ( e que } é como a ).Então você cria uma função (lcurly-brace-reader
) que o leitor chamará sempre que vir um {, e você usa set-macro-character
para atribuir essa função ao {.Então você diz ao Lisp que ( e ) são como [ e ] (isto é, sem sintaxe significativa).
Outras coisas que você pode fazer incluem, por exemplo, criando uma nova sintaxe de string ou usando [e] para incluir a notação fixa e processá-la em expressões S.
Você também pode ir muito além disso, redefinindo toda a sintaxe com seus próprios caracteres macro que irão desencadear ações no leitor, para que o céu seja realmente o limite.Esta é apenas uma das razões pelas quais Paulo Graham e outros continuo dizendo que Lisp é uma boa linguagem para escrever um compilador.
Eu não sou um especialista em Lisp, nem sou um programador Lisp, mas depois de experimentar um pouco com a linguagem cheguei à conclusão que depois de um tempo os parênteses começam a ficar 'invisíveis' e você começa a ver o código como você quer que seja.Você começa a prestar mais atenção às construções sintáticas que cria por meio de s-exprs e macros, e menos à forma lexical do texto das listas e parênteses.
Isto é especialmente verdadeiro se você aproveitar um bom editor que ajuda com o recuo e a coloração da sintaxe (tente definir o parêntese para uma cor muito semelhante ao fundo).
Talvez você não consiga substituir completamente o idioma e obter a sintaxe 'Ruby', mas não precisa disso.Graças à flexibilidade da linguagem, você pode acabar tendo um dialeto que parece seguir o 'estilo de programação Ruby', se quiser, seja lá o que isso signifique para você.
Eu sei que esta é apenas uma observação empírica, mas acho que tive um daqueles momentos de iluminação do Lisp quando percebi isso.
Repetidas vezes, os recém -chegados a Lisp querem "se livrar de todos os parênteses". Dura algumas semanas.Nenhum projeto para construir uma sintaxe de programação de uso geral grave sobre o analisador de expressão s usual de S de sempre chega a qualquer lugar, porque os programadores invariavelmente acabam preferindo o que você percebe atualmente como "parênteses infernal". Demora um pouco para se acostumar, mas não muito!Depois que você se acostumar e puder realmente apreciar a plasticidade da sintaxe padrão, voltar às linguagens onde só há uma maneira de expressar qualquer construção de programação específica é realmente irritante.
Dito isto, Lisp é um excelente substrato para a construção de linguagens específicas de domínio.Tão bom quanto, senão melhor, que XML.
Boa sorte!
A melhor explicação das macros Lisp que já vi está em
https://www.youtube.com/watch?v=4NO83wZVT0A
começando por volta dos 55 minutos.Este é o vídeo de uma palestra proferida por Peter Seibel, autor de "Practical Common Lisp", que é o melhor livro didático sobre Lisp que existe.
A motivação para macros Lisp geralmente é difícil de explicar, porque elas realmente se destacam em situações que são muito longas para serem apresentadas em um tutorial simples.Peter apresenta um ótimo exemplo;você pode compreendê-lo completamente e faz um uso bom e adequado das macros Lisp.
Você perguntou:"você poderia mudar a natureza funcional do LISP para uma sintaxe e semântica mais orientada a objetos".A resposta é sim.Na verdade, o Lisp originalmente não tinha nenhuma programação orientada a objetos, o que não é surpreendente, já que o Lisp já existe muito antes da programação orientada a objetos!Mas quando aprendemos sobre OOP pela primeira vez em 1978, conseguimos adicioná-lo facilmente ao Lisp, usando, entre outras coisas, macros.Eventualmente, o Common Lisp Object System (CLOS) foi desenvolvido, um sistema de programação orientado a objetos muito poderoso que se encaixa perfeitamente no Lisp.A coisa toda pode ser carregada como uma extensão – nada está embutido!Tudo é feito com macros.
Lisp possui um recurso totalmente diferente, chamado "leitor de macros", que pode ser usado para estender a sintaxe de superfície da linguagem.Usando macros de leitura, você pode criar sublinguagens com sintaxe semelhante a C ou Ruby.Eles transformam o texto em Lisp, internamente.Eles não são amplamente utilizados pela maioria dos programadores Lisp reais, principalmente porque é difícil estender o ambiente de desenvolvimento interativo para compreender a nova sintaxe.Por exemplo, os comandos de indentação do Emacs seriam confundidos por uma nova sintaxe.Porém, se você for enérgico, o Emacs também é extensível e você poderá ensiná-lo sobre sua nova sintaxe lexical.
Macros regulares operam em listas de objetos.Mais comumente, esses objetos são outras listas (formando assim árvores) e símbolos, mas podem ser outros objetos, como strings, hashtables, objetos definidos pelo usuário, etc.Essas estruturas são chamadas s-exps.
Portanto, quando você carrega um arquivo fonte, seu compilador Lisp irá analisar o texto e produzir s-exps.Macros operam sobre eles.Isso funciona muito bem e é uma maneira maravilhosa de estender a linguagem dentro do espírito do s-exps.
Além disso, o processo de análise mencionado acima pode ser estendido por meio de "macros de leitura" que permitem personalizar a maneira como seu compilador transforma texto em s-exps.Sugiro, entretanto, que você adote a sintaxe do Lisp em vez de transformá-la em outra coisa.
Você parece um pouco confuso ao mencionar a "natureza funcional" do Lisp e a "sintaxe orientada a objetos" do Ruby.Não tenho certeza do que deveria ser "sintaxe orientada a objetos", mas Lisp é uma linguagem multiparadigma e suporta programação orientada a objetos extremamente bem.
Aliás, quando digo Lisp, quero dizer Lisp comum.
Eu sugiro que você deixe seus preconceitos de lado e dê ao Lisp uma chance honesta.
Inferno entre parênteses?Não vejo mais parênteses em:
(function toto)
do que em:
function(toto);
E em
(if tata (toto)
(titi)
(tutu))
não mais do que em:
if (tata)
toto();
else
{
titi();
tutu();
}
Eu vejo menos colchetes e ';' no entanto.
O que você está perguntando é como se tornar um especialista em chocolate para poder remover toda aquela coisa marrom infernal do seu bolo de chocolate favorito.
Sim, você pode alterar fundamentalmente a sintaxe e até escapar do "inferno dos parênteses".Para isso você precisará definir uma nova sintaxe de leitor.Observe as macros do leitor.
Eu suspeito, no entanto, que para atingir o nível de conhecimento em Lisp para programar tais macros, você precisará mergulhar na linguagem a tal ponto que não considerará mais os parênteses como "inferno".Ou sejaquando você souber como evitá-los, você terá passado a aceitá-los como uma coisa boa.
Se você quiser que o lisp se pareça com Ruby, use Ruby.
É possível usar Ruby (e Python) de uma maneira muito semelhante ao lisp, que é uma das principais razões pelas quais eles ganharam aceitação tão rapidamente.
veja este exemplo de como as macros do leitor podem estender o leitor lisp com tarefas complexas, como modelos XML:
http://common-lisp.net/project/cl-quasi-quote/present-class.html
esse biblioteca do usuário compila as partes estáticas do XML em matrizes de bytes literais codificadas em UTF-8 em tempo de compilação que estão prontas para serem sequenciadas por gravação no fluxo da rede.e são utilizáveis em macros lisp normais, são ortogonais ...o posicionamento do caractere vírgula influencia quais partes são constantes e quais devem ser avaliadas em tempo de execução.
mais detalhes disponíveis em: http://common-lisp.net/project/cl-quasi-quote/
outro projeto que para extensões de sintaxe Common Lisp: http://common-lisp.net/project/cl-syntax-sugar/
@sparkes
Às vezes, LISP é a escolha de linguagem mais clara, ou seja, extensões Emacs.Tenho certeza de que poderia usar Ruby para estender o Emacs se quisesse, mas o Emacs foi projetado para ser estendido com LISP, então parece fazer sentido usá-lo nessa situação.
É uma pergunta complicada.Como o lisp já está estruturalmente tão próximo de uma árvore de análise, a diferença entre um grande número de macros e a implementação de sua própria minilinguagem em um gerador de análise não é muito clara.Mas, exceto pelo parêntese de abertura e fechamento, você pode facilmente acabar com algo que não se parece em nada com lisp.
Um dos usos de macros que me surpreendeu foi a verificação em tempo de compilação de solicitações SQL em relação ao banco de dados.
Depois que você perceber que tem a linguagem completa em tempo de compilação, isso abrirá novas perspectivas interessantes.O que também significa que você pode dar um tiro no próprio pé de novas maneiras interessantes (como tornar a compilação não reproduzível, o que pode facilmente se transformar em um pesadelo de depuração).