Criando Macros Com Syntax Quoting

, clojure, fp, macros

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: