LoginSignup
15
1

More than 1 year has passed since last update.

Macro.Env

__ENV__/0

現在の環境は、いつでも__ENV__/0で取得できます。

iex> __ENV__
#Macro.Env<
  aliases: [],
  context: nil,
  ...
>

iex> __ENV__ |> Map.keys()
[:__struct__, :aliases, :context, :context_modules, :file, :function,
 :functions, :lexical_tracker, :line, :macro_aliases, :macros, :module,
 :requires, :tracers, :versioned_vars]

直接アクセスしてはいけないフィールドがいくつかあります。Elixir内部のマクロ展開機構専用とのことです。

  • :aliases
  • :functions
  • :macro_aliases
  • :macros
  • :lexical_tracker
  • :requires
  • :tracers
  • :versioned_vars

とはいうものの現在の環境でどの関数が使えるのかが確認できると便利な場合がある気がします。
こっそり中身を見てみます。

iex> __ENV__.functions
[
  {IEx.Helpers,
   [
     break!: 3,
     break!: 4,
     breaks: 0,
     c: 1,
     c: 2,
     cd: 1,
     clear: 0,
     continue: 0,
     exports: 0,
     exports: 1,
     flush: 0,
     h: 0,
     i: 0,
     i: 1,
     l: 1,
     ls: 0,
     ls: 1,
     n: 0,
     next: 0,
     nl: 1,
     nl: 2,
     open: 0,
     pid: 1,
     pid: 3,
     port: 1,
     port: 2,
     pwd: 0,
     r: 1,
     recompile: 0,
     recompile: 1,
     ref: 1,
     ref: 4,
     remove_breaks: 0,
     remove_breaks: 1,
     reset_break: 1,
     reset_break: 3,
     respawn: 0,
     runtime_info: 0,
     runtime_info: 1,
     v: 0,
     v: 1,
     whereami: 0,
     whereami: 1
   ]},
  {Kernel,
   [
     !=: 2,
     !==: 2,
     *: 2,
     **: 2,
     +: 1,
     +: 2,
     ++: 2,
     -: 1,
     -: 2,
     --: 2,
     /: 2,
     <: 2,
     <=: 2,
     ==: 2,
     ===: 2,
     =~: 2,
     >: 2,
     >=: 2,
     abs: 1,
     apply: 2,
     apply: 3,
     binary_part: 3,
     binary_slice: 2,
     binary_slice: 3,
     bit_size: 1,
     byte_size: 1,
     ceil: 1,
     div: 2,
     elem: 2,
     exit: 1,
     floor: 1,
     function_exported?: 3,
     get_and_update_in: 3,
     get_in: 2,
     hd: 1,
     inspect: 1,
     inspect: 2,
     is_atom: 1,
     is_binary: 1,
     is_bitstring: 1,
     is_boolean: 1,
     is_float: 1,
     is_function: 1,
     is_function: 2,
     is_integer: 1,
     is_list: 1,
     is_map: 1,
     is_map_key: 2,
     is_number: 1,
     is_pid: 1,
     is_port: 1,
     is_reference: 1,
     is_tuple: 1,
     length: 1,
     macro_exported?: 3,
     make_ref: 0,
     map_size: 1,
     max: 2,
     min: 2,
     node: 0,
     node: 1,
     not: 1,
     pop_in: 2,
     put_elem: 3,
     put_in: 3,
     rem: 2,
     round: 1,
     self: 0,
     send: 2,
     spawn: 1,
     spawn: 3,
     spawn_link: 1,
     spawn_link: 3,
     spawn_monitor: 1,
     spawn_monitor: 3,
     struct: 1,
     struct: 2,
     struct!: 1,
     struct!: 2,
     throw: 1,
     tl: 1,
     trunc: 1,
     tuple_size: 1,
     update_in: 3
   ]}
]
iex>

__CALLER__/0

マクロの内部では、__CALLER__/0 により呼び出し元の環境が取得できます。__ENV__/0とは異なり、マクロの内部でしか使用できません。

iex> __CALLER__
** (CompileError) iex:3: __CALLER__ is available only inside defmacro and defmacrop
    (elixir 1.14.2) src/elixir.erl:376: :elixir.quoted_to_erl/4
    (elixir 1.14.2) src/elixir.erl:277: :elixir.eval_forms/4
    (elixir 1.14.2) lib/module/parallel_checker.ex:107: Module.ParallelChecker.verify/1
    (iex 1.14.2) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
    (iex 1.14.2) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
    (iex 1.14.2) lib/iex/evaluator.ex:292: IEx.Evaluator.parse_eval_inspect/3
    (iex 1.14.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1

試しに検証用マクロを書きます。

defmodule MyMacro do
  defmacro inspect_caller() do
    # 呼び出し元の環境
    IO.inspect(__CALLER__)

    # Elixir's AST (Abstract Syntax Tree)
    quote do
      IO.puts("hello")
    end
  end
end

require MyMacro

MyMacro.inspect_caller()

:tada:

Phoenix Framework

Phoenix Framework__ENV__/0__CALLER__/0が何箇所かで登場します。参考になるかもしれません。知らんけど。

ご参考までに

15
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
1