2015-08-08 32 views
7

Sto sperimentando con Macro in elisir. Pertanto, il codice che sto per mostrare dovrebbe essere fatto con semplici funzioni ma ... sto sperimentando!Espansione macro in elisir: come definire 2 macro con una utilizzando l'altra?

Voglio definire 2 macro (A e B) e fare A uso B per sperimentare l'espansione delle macro. Quando uso A, viene visualizzato un errore di compilazione in cui si dice che la funzione B è indefinita.

Ecco il codice:

defmodule MyMacros do 
    defmacro print_expr(expr) do 
    quote do 
     IO.puts(unquote(expr)) 
    end 
    end 

    defmacro print_hashes_around(expr) do 
    quote do 
     IO.puts "###" 
     print_expr(unquote(expr)) 
     IO.puts "###" 
    end 
    end 
end 

defmodule MyModule do 
    require MyMacros 

    def my_print(expr) do 
    MyMacros.print_hashes_around(expr) 
    end 
end 

MyModule.my_print("hello world") 

E qui è l'errore di compilazione:

macro_test.exs:17: warning: redefining module MyModule 
** (CompileError) macro_test.exs:21: function print_expr/1 undefined 
(stdlib) lists.erl:1336: :lists.foreach/2 
macro_test.exs:17: (file) 
(elixir) lib/code.ex:307: Code.require_file/2 

Il mio modo di (mis) capire le cose:

  1. Con MyMacros richiedono, il modulo MyModule dovrebbe conoscere l'esistenza di entrambe le macro. Pertanto dovrei essere in grado di utilizzare qualsiasi macro.
  2. Quando print_hashes_around viene espanso in MyModule, il compilatore dovrebbe trovare che print_expr è anche una macro. Pertanto, dovrebbe verificarsi un'altra espansione.
  3. Ciò che sembra accadere è che la seconda espansione non avviene. Pertanto il compilatore cerca una definizione di funzione che non esiste.

Ho ragione?

Come suggerito nel gioco, il prefisso print_expr con MyMacros. lo corregge. Ancora non capisco perché. MyModule richiede MyMacros quindi entrambe le macro dovrebbero essere conosciute ed espandibili ... Quando guardo la definizione di unless, utilizza if, non Kernel.if.

risposta

9

L'equivoco è qui. :) require rende il modulo disponibile solo al compilatore, non importa l'importazione le funzioni del modulo. Se hai usato import MyModule allora funzionerebbe.

Tuttavia, sarebbe meglio risolvere il problema anteponendo il nome del modulo, in quanto consente agli sviluppatori di utilizzare il codice per utilizzare le macro in modo esplicito (con require) o importandole.

Un'altra opzione è quella di evitare molteplici invocazioni macro in questo modo:

defmodule MyMacros do 
    defmacro print_expr(expr) do 
    quoted_print_expr(expr) 
    end 

    defmacro print_hashes_around(expr) do 
    quote do 
     IO.puts "###" 
     unquote(quoted_print_expr(expr)) 
     IO.puts "###" 
    end 
    end 

    defp quoted_print_expr(expr) do 
    quote do 
     IO.puts(unquote(expr)) 
    end 
    end 
end 
+1

Grazie José, è sorprendente per ottenere una risposta dalla lingua Creatore stesso! – svarlet