Функции самоанализа с защитными предложениями

Учитывая, что модуль имеет две функции с одинаковой арностью, но разные защитные предложения, как мне (в идеале) увидеть, что это за предложения, или, по крайней мере, что есть две функции?

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) не работает, так как возвращает только [greet: 1]


person AnilRedshift    schedule 22.05.2018    source источник


Ответы (2)


Вы можете декомпилировать код модуля в «абстрактный код» и вникнуть в него, чтобы получить эту информацию. Вот как вы можете получить предложения каждой функции в модуле:

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.

person Dogbert    schedule 23.05.2018

Если модуль сторонний и / или уже скомпилирован, обратитесь к ответу, предоставленному @Dogbert.

Если модуль принадлежит владельцу, запрошенная информация может быть собрана на этапе компиляции с помощью _1 _ крючок:

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 для хранения информации, потому что она хранится на этапе компиляции и обычно используется во время выполнения.

person Aleksei Matiushkin    schedule 23.05.2018