O Lisp comum permite que você escreva macros que fazem a transformação de origem desejada.
O esquema fornece um sistema higiênico de correspondência de padrões que permite executar transformações também. Quão úteis são as macros na prática? Paul Graham disse em Beating the Averages que:
O código fonte do editor da Viaweb era provavelmente de 20 a 25% de macros.
Que tipo de coisas as pessoas realmente acabam fazendo com macros?
Respostas:
Dê uma olhada nesta postagem de Matthias Felleisen na lista de discussão do LL1 em 2002. Ele sugere três usos principais para macros:
fonte
Eu uso principalmente macros para adicionar novas construções de linguagem que economizam tempo, que exigiriam um monte de código padrão.
Por exemplo, recentemente me vi querendo um imperativo
for-loop
semelhante ao C ++ / Java. No entanto, sendo uma linguagem funcional, Clojure não veio com uma fora da caixa. Então, eu apenas o implementei como uma macro:E agora eu posso fazer:
E aí está: uma nova construção de linguagem de tempo de compilação de uso geral em seis linhas de código.
fonte
Extensões de linguagem de escrita ou DSLs.
Para ter uma idéia disso em idiomas do tipo Lisp, estude o Racket , que possui várias variantes de idioma: Raquete Digitada, R6RS e Registro de Dados.
Consulte também o idioma Boo, que fornece acesso ao pipeline do compilador com o objetivo específico de criar idiomas específicos do domínio por meio de macros.
fonte
aqui estão alguns exemplos:
Esquema:
define
para definições de função. Basicamente, faz uma maneira mais curta de definir uma função.let
para criar variáveis com escopo lexical.Clojure:
defn
, de acordo com seus documentos:O mesmo que (nome do def (fn [params *] exprs *)) ou (nome do def (fn ([params *] exprs *) +)) com qualquer sequência de caracteres ou atributos adicionados aos metadados var
for
: compreensão da listadefmacro
: irônico?defmethod
,defmulti
: trabalhando com vários métodosns
Muitas dessas macros tornam muito mais fácil escrever código em um nível mais abstrato. Penso que as macros são semelhantes, em muitos aspectos, à sintaxe em não Lisps.
A biblioteca de plotagem Incanter fornece macros para algumas manipulações de dados complexas.
fonte
Macros são úteis para incorporar alguns padrões.
Por exemplo, Common Lisp não define o
while
loop, mas possui odo
que pode ser usado para defini-lo.Aqui está um exemplo do On Lisp .
Isso imprimirá "12345678910" e se você tentar ver o que acontece com
macroexpand-1
:Isso retornará:
Essa é uma macro simples, mas como dito anteriormente, elas geralmente são usadas para definir novas linguagens ou DSLs, mas, a partir deste exemplo simples, você já pode imaginar o que pode fazer com elas.
A
loop
macro é um bom exemplo do que as macros podem fazer.O Lisp comum possui outro tipo de macros chamado macro de leitor, que pode ser usado para modificar a maneira como o leitor interpreta o código, ou seja, você pode usá-los para usar # {e #} possui delimitadores como # (e #).
fonte
Aqui está um que eu uso para depuração (no Clojure):
Eu tive que lidar com uma tabela de hash rolada à mão em C ++, onde o
get
método utilizou uma referência de cadeia não-const como argumento, o que significa que não posso chamá-lo com um literal. Para facilitar isso, escrevi algo como o seguinte:Embora seja improvável que algo como esse problema apareça no lisp, acho particularmente bom que você possa ter macros que não avaliam seus argumentos duas vezes, por exemplo, introduzindo uma verdadeira let-binding. (Admitido, aqui eu poderia ter contornado isso).
Também recorro ao truque horrivelmente feio de embrulhar coisas de
do ... while (false)
tal maneira que você possa usá-las na parte de um if e ainda assim fazer o resto funcionar como esperado. Você não precisa disso no lisp, que é uma função de macros operando em árvores de sintaxe, em vez de seqüências de caracteres (ou sequências de token, eu acho, no caso de C e C ++), que passam por análise.Existem algumas macros de encadeamento embutidas que podem ser usadas para reorganizar seu código, para que ele seja lido de maneira mais limpa ('encadeamento' como em 'semear o código', não paralelismo). Por exemplo:
Ele pega a primeira forma,
(range 6)
e a torna o último argumento da próxima forma,(filter even?)
que por sua vez é o último argumento da próxima forma e assim por diante, de modo que o acima seja reescrito emAcho que o primeiro lê com muito mais clareza: "pegue esses dados, faça isso, depois faça isso, depois faça o outro e pronto", mas isso é subjetivo; algo que é objetivamente verdadeiro é que você leia as operações na sequência em que são executadas (ignorando a preguiça).
Há também uma variante que insere o formulário anterior como o primeiro (e não o último) argumento. Um caso de uso é aritmético:
Lê como "pegue 17, subtraia 2 e divida por 3".
Falando em aritmética, você pode escrever uma macro que faz análise de notação de infixo, para que você possa dizer, por exemplo,
(infix (17 - 2) / 3)
e cuspirá(/ (- 17 2) 3)
qual tem a desvantagem de ser menos legível e a vantagem de ser uma expressão lisp válida. Essa é a parte de sub-linguagem DSL / dados.fonte