Sou novo no Clojure, sou novo no Macros e não tenho experiência anterior no Lisp. Eu criei meu próprio caso de switch como formulário e acabei com isso:
(defmacro switch-case [v cases default] (if (cases v) (cases v) default ))
e então tentei criar uma função e acabei com isso:
(defn fn-switch-case [v cases default] (if (cases v) (cases v) default ))
Ambos
(switch-case 5 {6 "six" 7 "seven"} "not there")
e
(fn-switch-case 5 {6 "six" 7 "seven"} "not there")
Funciona bem.
Qual poderia ser o cenário em que eu precisaria de uma macro e uma função não funcionam? Existe uma desvantagem na minha função ou implementações de macro?
Respostas:
Um benefício das macros é que elas não seguem as mesmas regras de avaliação que as funções, pois estão apenas realizando transformações no código.
Um exemplo de construção que você não pode criar usando apenas funções é a macro de encadeamento do Clojure .
Permite inserir uma expressão como o primeiro argumento em uma sequência de formulários:
é equivalente a
Você não seria capaz de criar esse tipo de construção usando apenas funções, pois antes da
->
expressão ser avaliada, o interior(+ 3)
e(* 4)
o significado->
seriam recebidos.e precisa ver as funções reais sendo usadas para funcionar.
No seu exemplo, considere se o resultado de algum caso teve efeitos colaterais ou chame alguma outra função. Uma versão de função não seria capaz de impedir que esse código fosse executado em uma situação em que esse resultado não fosse selecionado, enquanto uma macro poderia impedir que esse código fosse avaliado, a menos que fosse a opção escolhida.
fonte
Um aspecto divertido das macros é que elas oferecem a possibilidade de expandir a sintaxe do seu lisp e adicionar novos recursos sintáticos a ela, e isso acontece apenas porque os argumentos passados para uma macro são avaliados apenas no tempo de execução, e não no momento da compilando sua macro. Como exemplo, as primeiras / últimas macros de thread no clojure
-> ->>
não avaliam seus argumentos de expressão, caso contrário, esses resultados avaliados não poderiam aceitar mais nada (digamos(+ 3) => 3
e3
não é uma função que poderia aceitar seu primeiro argumento principal em algo como(-> 4 (+ 3))
).Agora, se eu gosto dessa sintaxe e quero adicioná-la à minha implementação Common Lisp (que não a possui), posso adicioná-la definindo uma macro por conta própria. Algo assim:
Agora eu seria capaz de usá-los no Common Lisp da mesma maneira que no Clojure:
Talvez você também queira ter uma nova sintaxe para a
range
função do clojure com suas próprias palavras-chave para sua sintaxe, algo como:pode ser definido como esta macro:
ou adicione algo semelhante ao Common Lisp (é apenas por exemplo, pois tudo isso já pode ser feito nos idiomas, é claro!):
fonte
Não sei exatamente o que você deseja que sua função de caixa de comutação faça. Qual deve ser o valor de:
Pode ser útil se você visualizou a fonte das funções / macros principais relacionadas, usando o comando repl
Mas, em geral, uma razão pela qual você pode querer uma macro em vez de uma função é que o uso da
case
macro principalnão imprimirá olá. No entanto, se
case
definido como uma função, ele imprimirá "olá" na tela e toda a expressão será avaliada como"not there"
. Isso ocorre porque as funções avaliam seus argumentos antes de cada chamada da função.fonte