使用保护子句自省函数
Introspect functions with guard clauses
给定一个模块具有两个具有相同数量但不同保护子句的函数,我如何(理想情况下)查看这些子句是什么,或者至少有两个函数?
defmodule Test do
def greet(name) when name == "foo" do
IO.puts("Hello, bar")
end
def greet(name), do: IO.puts("Hello, #{name}")
end
Test.__info__(:functions)
不起作用,因为它只有 returns [greet: 1]
您可以将模块的代码反编译为 "abstract code" 并深入研究以获取此信息。以下是获取模块中每个函数的子句的方法:
module = Test
{:ok, {^module, [abstract_code: {:raw_abstract_v1, abstract_code}]}} = :beam_lib.chunks(module, [:abstract_code])
for {:function, _, name, arity, clauses} <- abstract_code do
# Uncomment the next line to print the AST of the clauses.
# IO.inspect(clauses)
IO.inspect {name, arity, length(clauses)}
end
输出:
{:__info__, 1, 7}
{:greet, 1, 2}
注意:这可能是私有的 API,可能会在 Erlang/OTP 的未来版本中发生变化。上面的输出是在 Erlang/OTP 20.
如果模块是3rd-party and/or已经编译,请参考@Dogbert提供的答案。
如果拥有该模块,则可能会在编译阶段使用 @on_definition
挂钩收集请求的信息:
defmodule TestInfo do
def on_definition(_env, kind, name, args, guards, body) do
with {:ok, table} <- :dets.open_file(:test_info, type: :set) do
clauses =
case :dets.lookup(table, name) do
{:error, _reason} -> []
[] -> []
list when is_list(list) -> list[name]
end
:dets.insert(table, {name, [{kind, args, guards} | clauses]})
:dets.close(table)
end
end
end
defmodule Test do
@on_definition {TestInfo, :on_definition} # ⇐ THIS
def greet(name) when name == "foo" do
IO.puts("Hello, bar")
end
def greet(name), do: IO.puts("Hello, #{name}")
end
现在您已将所有定义存储在 DETS 中:
{:ok, table} = :dets.open_file(:test_info, type: :set)
:dets.lookup(table, :greet)
#⇒ [
# greet: [
# {:def, [{:name, [line: 10], nil}], []},
# {:def, [{:name, [line: 6], nil}],
# [{:==, [line: 6], [{:name, [line: 6], nil}, "foo"]}]}]
# ]
:dets.close(table)
我使用 DETS 来存储信息,因为它存储在 编译 阶段,典型的用法是在运行时。
给定一个模块具有两个具有相同数量但不同保护子句的函数,我如何(理想情况下)查看这些子句是什么,或者至少有两个函数?
defmodule Test do
def greet(name) when name == "foo" do
IO.puts("Hello, bar")
end
def greet(name), do: IO.puts("Hello, #{name}")
end
Test.__info__(:functions)
不起作用,因为它只有 returns [greet: 1]
您可以将模块的代码反编译为 "abstract code" 并深入研究以获取此信息。以下是获取模块中每个函数的子句的方法:
module = Test
{:ok, {^module, [abstract_code: {:raw_abstract_v1, abstract_code}]}} = :beam_lib.chunks(module, [:abstract_code])
for {:function, _, name, arity, clauses} <- abstract_code do
# Uncomment the next line to print the AST of the clauses.
# IO.inspect(clauses)
IO.inspect {name, arity, length(clauses)}
end
输出:
{:__info__, 1, 7}
{:greet, 1, 2}
注意:这可能是私有的 API,可能会在 Erlang/OTP 的未来版本中发生变化。上面的输出是在 Erlang/OTP 20.
如果模块是3rd-party and/or已经编译,请参考@Dogbert提供的答案。
如果拥有该模块,则可能会在编译阶段使用 @on_definition
挂钩收集请求的信息:
defmodule TestInfo do
def on_definition(_env, kind, name, args, guards, body) do
with {:ok, table} <- :dets.open_file(:test_info, type: :set) do
clauses =
case :dets.lookup(table, name) do
{:error, _reason} -> []
[] -> []
list when is_list(list) -> list[name]
end
:dets.insert(table, {name, [{kind, args, guards} | clauses]})
:dets.close(table)
end
end
end
defmodule Test do
@on_definition {TestInfo, :on_definition} # ⇐ THIS
def greet(name) when name == "foo" do
IO.puts("Hello, bar")
end
def greet(name), do: IO.puts("Hello, #{name}")
end
现在您已将所有定义存储在 DETS 中:
{:ok, table} = :dets.open_file(:test_info, type: :set)
:dets.lookup(table, :greet)
#⇒ [
# greet: [
# {:def, [{:name, [line: 10], nil}], []},
# {:def, [{:name, [line: 6], nil}],
# [{:==, [line: 6], [{:name, [line: 6], nil}, "foo"]}]}]
# ]
:dets.close(table)
我使用 DETS 来存储信息,因为它存储在 编译 阶段,典型的用法是在运行时。