2015-08-08 18 views
6

Dlaczego Elixir raportuje UndefinedFunctionError podczas wywoływania makra przy użyciu składni Module.macroName w pliku .exs? Wydaje mi się, że mogę wywołać makro tylko wtedy, gdy mam inną funkcję, która wywołuje makro i wywołuję funkcję zamiast makra.Eliksir - wywoływanie/wywoływanie makr - UndefinedFunctionError

Poniższy kod demonstruje to:

defmodule Sample do 
    defmacro doIt(expression) do 
    quote do 
     IO.puts unquote(expression) 
    end 
    end 

    def doFunc(e), do: doIt(e) 
end 

Sample.doFunc "Hello World via Function" # Works fine 
Sample.doIt "Hello World from Macro!" # Gives error 

Wyjście

Hello World via Function 
** (UndefinedFunctionError) undefined function: Sample.doIt/1 
    Sample.doIt("Hello World from Macro!") 
    (elixir) lib/code.ex:307: Code.require_file/2 

Przykład Elixir documentation wykorzystuje iex, zamiast dzwonić makro w pliku .exs. Nawet powyżej kodu, jeśli usuniemy połączenia do Sample.doIt i załadujemy je w iex, a następnie zadzwonimy pod numer Sample.doIt działa dobrze.

E:\elixir>iex hello.exs 
Hello World via Function 
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help) 
iex(1)> require Sample 
nil 
iex(2)> Sample.doIt "Hello" 
Hello 
:ok 
iex(3)> 

Gdy próbuję require Sample w moim pliku powyżej, jak poniżej

defmodule Sample 
    ... rest of stuff as shown above ... 
end 

require Sample 

Sample.doFunc "Hello World via Function" 
Sample.doIt "Hello World from Macro!" 

mi się błąd

** (CompileError) hello.exs:11: module Sample is not loaded but was defined. This happens because you are trying to use a module in the same context it is defined. Try defining the module outside the context that requires it. 
    (stdlib) lists.erl:1352: :lists.mapfoldl/3 
    (stdlib) lists.erl:1353: :lists.mapfoldl/3 

Odpowiedz

4

Jak zwykle trzeba wymagać moduł przed użyciem swoje makra oznaczać do kompilatora kolejność kompilacji modułów:

defmodule Other do 
    def run do 
    require Sample 

    Sample.doFunc "Hello World via Function" 
    Sample.doIt "Hello World from Macro!" 
    end 
end 

Other.run # => Hello World via Function 
      # Hello World from Macro! 
+0

Tutaj również zdefiniowałeś funkcję 'run' w innym module, aby użyć' Sample.doIt' - czy jest to jedyny sposób użycia makra - wywołaj go przez inną funkcję, która jest zawarta w Module –

+3

być ograniczeniem kompilatora. Jak informuje błąd - nie można używać makr z modułu w tym samym kontekście, w którym moduł jest zdefiniowany. Zgaduję, że ma to związek z tym, jak kompilator zamawia rzeczy do kompilacji. –

+3

Tak, dokładnie. Makra są rozszerzane w czasie kompilacji, więc gdy parsujemy 'defmodule Foo', a następnie' Foo.some_macro', nadal nie skompilowaliśmy 'Foo', więc nie mamy pojęcia, jak rozwinąć makro. –