Elixir Macros with Using
The __using__ macro runs when another module says use. It inserts shared code into the module that says use, shaped by the options given.
use inserts shared code
TeamB says use with no options. It gets the default greeting.
defmodule GreeterBase do
defmacro __using__(opts) do
greeting = Keyword.get(opts, :greeting, "Hello")
quote do
def greet(name), do: unquote(greeting) <> ", " <> name
end
end
end
defmodule TeamB do
use GreeterBase
end
IO.puts(TeamB.greet("Bo"))Options shape the inserted code
TeamA passes a different greeting. Its greet uses that greeting.
defmodule GreeterBase do
defmacro __using__(opts) do
greeting = Keyword.get(opts, :greeting, "Hello")
quote do
def greet(name), do: unquote(greeting) <> ", " <> name
end
end
end
defmodule TeamA do
use GreeterBase, greeting: "Hi"
end
IO.puts(TeamA.greet("Ana"))One use can inject several functions
The base injects greet and farewell together.
defmodule GreeterBase do
defmacro __using__(opts) do
greeting = Keyword.get(opts, :greeting, "Hello")
quote do
def greet(name), do: unquote(greeting) <> ", " <> name
def farewell(name), do: "Goodbye, " <> name
end
end
end
defmodule TeamC do
use GreeterBase, greeting: "Hey"
end
IO.puts(TeamC.greet("Ana"))
IO.puts(TeamC.farewell("Ana"))Example
defmodule GreeterBase do
defmacro __using__(opts) do
greeting = Keyword.get(opts, :greeting, "Hello")
quote do
def greet(name), do: unquote(greeting) <> ", " <> name
def farewell(name), do: "Goodbye, " <> name
end
end
end
defmodule TeamA do
use GreeterBase, greeting: "Hi"
end
defmodule TeamB do
use GreeterBase
end
IO.puts(TeamA.greet("Ana"))
IO.puts(TeamA.farewell("Ana"))
IO.puts(TeamB.greet("Bo"))
IO.puts(TeamB.farewell("Bo"))Key points
- __using__: __using__ is the macro that runs for a use call.
- Use: use Module, option: value calls the __using__ macro of that module with those options.
- Options: Keyword.get reads one option from the options list, with a default when it is missing.
- Injected code: the code returned by __using__ becomes part of the module that says use.
- Shared behaviour: one __using__ definition can give many modules the same functions.
In this example, the base defines __using__ with a greeting option, TeamA uses it with Hi, TeamB uses it with the default, and each module prints its own greet and farewell.
Note
use runs once per module, so each module gets its own copy of the injected code.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does use GreeterBase, greeting: "Hi" do?