Criando Macros Com Syntax Quoting
No último post falei um pouco sobre macros. Como macros é um assunto um pouco extenso, vou escrever mais alguns posts sobre detalhes avançados que podemos utilizar no dia-a-dia.
Só relembrando: Macros permitem estender a própria linguagem, ou seja, escrever a própria linguagem do jeito que você quiser. Podemos pegar como exemplo a macro if-not
:
Essa macro é built-in na própria linguagem, mas caso não existesse, ao invés de chamar a função if
invertendo o valor booleano (if (not (zero? 0)) :then :else)
, poderíamos criar a mesma macro e deixar a chamada de código mais concisa (if-not (zero? 0) :then :else)
.
A macro when
é outro bom exemplo de como uma macro pode ajudar o código a ficar mais conciso:
Caso a condição passada seja true
, um bloco com múltiplas chamadas é executado, similar ao do
. Na real, por baixo dos panos, a chamada na macro when
, retorna uma lista que será evaluated
. Mas que lista é esta? Vamos verificar usando a função macroexpand:
Como eu falei, nada mais que uma lista com chamadas para if
e do
. Isso acontece porque precisamos que uma lista seja retornada. Para que o Clojure possa dar evaluate
nela, como se fora uma chamada para uma função qualquer, por exemplo: (+ 1 2) ; => 2
.
Dentro de uma macro podemos usar qualquer outro código Clojure, chamar funções, por exemplo. Mas por que o if
e o do
não são evaluated? Porque usamos Simple Quoting para informar a linguagem que aquele pedaço de código não deve ser evaluated.
Vamos criar uma macro chamada unless
(ok, já sei, existe o if-not
) que recebe uma condicional e executa o bloco passado, que pode ser mais de uma lista, caso a condição seja false
:
Tivemos que fazer o quote do if
para dizer que ele é um símbolo unevaluated
na lista retornada. Porém, o código não ficou tão logível quanto deveria ficar, dependendo do tamanha da macro o código pode se tornar muito verboso e quase inelegível.
Para estes casos, podemos utilizar Syntax Quoting, um tipo de quote mais poderoso:
Syntax Quoting permite que uma lista unevaluated seja retornada, dando o mesmo efeito do Simple Quoting, mas com um código bem mais sucinto.
Uma das diferenças é que os símbolos são retornados com seu nome completo, por exemplo:
Olhando o retorno da macro unless
percebemos algo errado. Os parâmetros test
e branches
foram quoted também, quando na verdade deveriam ser (> 2 1)
e (println "If invertido")
. Se tentarmos invocar a macro acima, uma exceção informando que os símbolos não existem.
Podemos informar ao Clojure que faça unquote utilizando til (~):
Quando um til aparece dentro de um código syntax quoted, o Clojure faz um evaluate destes forms. No próximo post falaremos sobre Unquote Splicing.
Comentários: