【问题标题】:Introspect functions with guard clauses使用保护子句自省函数
【发布时间】:2018-11-01 19:07:55
【问题描述】:

给定一个具有两个具有相同数量但不同保护子句的函数的模块,我如何(理想情况下)查看这些子句是什么,或者至少有两个函数?

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]

【问题讨论】:

    标签: elixir introspection guard-clause


    【解决方案1】:

    您可以将模块的代码反编译为“抽象代码”并深入研究以获取此信息。以下是获取模块中每个函数的子句的方法:

    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 上的。

    【讨论】:

      【解决方案2】:

      如果模块是 3rd-party 和/或已经编译,请参考@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 来存储信息,因为它被存储在 编译 阶段,典型的用法是在运行时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-08
        • 1970-01-01
        • 2018-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多