首页 热点资讯 义务教育 高等教育 出国留学 考研考公
您的当前位置:首页正文

Elixir元编程

2024-12-15 来源:华拓网

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
显示全文