Meta-Programming
The Language of Macros
quote do: 1 + 2
quote do: div(10, 2)
defmodule Math do
defmacro say({:+, _, [lhs, rhs]}) do
quote do
lhs = unquote(lhs)
rhs = unquote(rhs)
result = lhs + rhs
IO.puts("#{lhs} plus #{rhs} is #{result}")
result
end
end
defmacro say({:*, _, [lhs, rhs]}) do
quote do
lhs = unquote(lhs)
rhs = unquote(rhs)
result = lhs * rhs
IO.puts("#{lhs} times #{rhs} is #{result}")
result
end
end
end
require Math
Math.say(5 + 2)
Math.say(18 * 4)
defmodule ControlFlow do
defmacro unless(expression, do: block) do
quote do
if !unquote(expression), do: unquote(block)
end
end
end
require ControlFlow
ControlFlow.unless(2 == 5, do: "block entered")
require ControlFlow
ControlFlow.unless 5 == 5 do
"block entered"
end
quote do
a = unquote(10)
IO.inspect(a)
end
number = 5
ast =
quote do
number * 10
end
Code.eval_quoted(ast)
number = 5
ast =
quote do
unquote(number) * 10
end
Code.eval_quoted(ast)
require ControlFlow
ast =
quote do
ControlFlow.unless(2 == 5, do: "block entered")
end
expanded_once = Macro.expand_once(ast, __ENV__)
IO.inspect(expanded_once)
expanded_fully = Macro.expand_once(expanded_once, __ENV__)
defmodule Mod do
defmacro definfo do
IO.puts("In macro's context (#{__MODULE__}).")
quote do
IO.puts("In caller's context (#{__MODULE__}).")
def friendly_info do
IO.puts("""
My name is #{__MODULE__}
My functions are #{inspect(__MODULE__.__info__(:functions))}
""")
end
end
end
end
defmodule MyModule do
require Mod
Mod.definfo()
end
MyModule.friendly_info()
ast =
quote do
if meaning_to_life == 42 do
"it's true"
else
"it remains to be seen"
end
end
Code.eval_quoted(ast, meaning_to_life: 42)
ast =
quote do
if var!(meaning_to_life) == 42 do
"it's true"
else
"it remains to be seen"
end
end
IO.inspect(Code.eval_quoted(ast, meaning_to_life: 42))
Code.eval_quoted(ast, meaning_to_life: 100)
defmodule Setter do
defmacro bind_name(string) do
quote do
name = unquote(string)
end
end
end
require Setter
name = "Chris"
Setter.bind_name("Max")
name
defmodule Setter2 do
defmacro bind_name(string) do
quote do
var!(name) = unquote(string)
end
end
end
require Setter2
name = "Chris"
Setter2.bind_name("Max")
name