Quote and unquote
一个Elixir程序可以被它自己的数据结构表示(数据即代码,我好像听说在一门远古传说的l开头的语言中听说过,它神秘而强大)。这章将学习宏。
Quoting
一行普通可以执行的代码可以被quote表示:
iex> quote do: sum(1, 2, 3)
{:sum, [], [1, 2, 3]}
iex> quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
iex> quote do: %{1 => 2}
{:%{}, [], [{1, 2}]}
Unquoting
Unquoting就是把表达式解析成本来的形式
iex> number = 13
iex> Macro.to_string(quote do: 11 + (number))
iex> Macro.to_string(quote do: 11 + unquote(number))
"11 + 13"
Escaping
我也不知道是什么
宏
在elixir中,宏使用defmacro定义
macros.exs
defmodule Unless do
def fun_unless(clause, expression) do
if(!clause, do: expression)
end
defmacro macro_unless(clause, expression) do
quote do
if(!unquote(clause), do: unquote(expression))
end
end
end
使用
iex> require Unless
iex> Unless.macro_unless true, IO.puts "this should never be printed"
nil
iex> Unless.fun_unless true, IO.puts "this should never be printed"
"this should never be printed"
nil
注意,上面fun_unless打印了语句,但是macro_unless版本却没有。这是因为宏没有执行它的参数,但是函数执行了。
使用 Macro.expand_once/2 可以检查代码生成的结果.
iex> expr = quote do: Unless.macro_unless(true, IO.puts "this should never be printed")
iex> res = Macro.expand_once(expr, __ENV__)
iex> IO.puts Macro.to_string(res)
if(!true) do
IO.puts("this should never be printed")
end
:ok
unless在elixir中也是一个宏
defmacro unless(clause, options) do
quote do
if(!unquote(clause), do: unquote(options))
end
end
宏安全
Elixir的宏最近进化了,它保证不会影响同一作用域的同名变量。
defmodule Hygiene do
defmacro no_interference do
quote do: a = 1
end
end
defmodule HygieneTest do
def go do
require Hygiene
a = 13
Hygiene.no_interference
a
end
end
HygieneTest.go
但是如果你其实想影响,可以用var!
defmodule Hygiene do
defmacro interference do
quote do: var!(a) = 1
end
end
defmodule HygieneTest do
def go do
require Hygiene
a = 13
Hygiene.interference
a
end
end
HygieneTest.go