【问题标题】:Does Elixir support introspection to show function origins?Elixir 是否支持自省来显示函数起源?
【发布时间】:2015-12-17 01:32:42
【问题描述】:

如果一个模块imports 有多个其他模块,那么给定函数的来源可能并不明显。例如:

defmodule Aimable do
  import Camera
  import Gun

  def trigger do
    shoot # which import brought it in?
  end
end

我知道有一些方法可以最大程度地减少这种混淆:良好的命名、专注的模块、像import Gun, only: [:shoot] 这样的定向导入等等。

但是如果我遇到这样的代码,有没有办法检查Aimable 并查看函数shoot 的起源?

【问题讨论】:

  • 我来自 Ruby,Array.instance_method(:reduce).owner 返回 Enumerable,这告诉我 Array 从哪里继承方法。

标签: elixir


【解决方案1】:

你可以直接这样做:

# from inside the module; IO.inspect(&Aimable.shoot/0) reveals nothing
IO.inspect &shoot/0 #=> &Gun.shoot/0

Check this out

还请记住,您不能在两个不同的模块中具有相同的函数名称和相同的数量,并将它们都导入另一个模块。这将导致调用该函数时出现歧义错误。

另一种痛苦的方式。您可以使用function_exported?/3.。规格:

function_exported?(atom | tuple, atom, arity) :: boolean

如果模块已加载并包含具有给定数量的公共函数,则返回 true,否则返回 false。

例子:

function_exported?(Gun,    :shoot, 0) #=> true
function_exported?(Camera, :shoot, 0) #=> false

【讨论】:

    【解决方案2】:

    使用__ENV__

    据我了解on another question__ENV__ 宏可以访问various environment info,包括__ENV__.functions__ENV__.macros

    __ENV__.functions 返回模块元组列表和它们提供的函数列表,例如:

    [{Some.module, [do_stuff: 2]}, {Other.Module, [a_function: 2, a_function: 3]}]
    

    您可以直观地扫描此shoot,或编写代码进行搜索。

    【讨论】:

      【解决方案3】:

      我正在使用 Elixir 1.1.0,您所描述的内容似乎是不允许的。这是脚本(在 aimable.ex 中):

      defmodule Gun do
        def shoot do
          IO.puts "Gun Shot"
        end
      end
      
      defmodule Camera do
        def shoot do
          IO.puts "Camera Shot"
        end
      end
      
      defmodule Aimable do
        import Camera
        import Gun
      
        def trigger do
          shoot
        end
      end
      

      现在当我运行 iex aimable.ex 时,我得到一个 CompileError

      Erlang/OTP 18 [erts-7.1] [来源] [64 位] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

      ** (CompileError) aimable.ex:18: 从 Camera 和 Gun 导入的函数 shoot/0,调用不明确 (elixir) src/elixir_dispatch.erl:111: :elixir_dispatch.expand_import/6 (elixir) src/elixir_dispatch.erl:82: :elixir_dispatch.dispatch_import/5

      【讨论】:

      • 你说得对,不能从多个模块导入shoot/0。但是,我的问题有点不同。当然,如果我查看Aimable,我知道shoot 必须来自CameraGun,但不能同时来自两者。但是,如果不扫描源代码,我怎么知道是哪一个呢?那可能很复杂。考虑 Phoenix 控制器,use SomeApp.Web, :controller,它又拥有自己的引用 importuse 指令。我可以反省一下render 的来源以便查找文档吗?
      • 鉴于@coderVidal 在他的回答中提供了一种内省相关函数的方法,为了完整起见,我只是添加了我的答案,以强调您不能导入具有相同名称的函数的 2 个模块和arity。
      【解决方案4】:

      另一种可能的技术(为完整起见添加):

      defmodule Aimable do
        import Camera, :only [shoot: 0]
        import Gun
      
      #etc.
      

      defmodule Aimable do
        import Camera
        import Gun, :only [shoot: 0]
      
      # etc.
      

      然后看看哪个编译不正确。

      只是实现这一目标的另一种方式。

      【讨论】:

        猜你喜欢
        • 2022-11-26
        • 1970-01-01
        • 2022-11-18
        • 1970-01-01
        • 2013-01-24
        • 1970-01-01
        • 1970-01-01
        • 2021-04-16
        • 1970-01-01
        相关资源
        最近更新 更多