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()
Phoenix Framework
Phoenix Frameworkで__ENV__/0
と__CALLER__/0
が何箇所かで登場します。参考になるかもしれません。知らんけど。
ご参考までに