Does Elixir support introspection to show function origins?

荒凉一梦 提交于 2019-12-22 06:09:25

问题


If a module imports multiple other modules, it may not be obvious where a given function came from. For example:

defmodule Aimable do
  import Camera
  import Gun

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

I know there are ways to minimize this kind of confusion: good naming, focused modules, targeted imports like import Gun, only: [:shoot], etc.

But if I come across code like this, is there a way to inspect Aimable and see the origins of the function shoot?


回答1:


You can do this directly:

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

Check this out

Also remember that you cannot have same function names with same arity in two different modules and import them both in another module. This will result in ambiguity error when calling that function.

Another painful way. You can use function_exported?/3.. Specs:

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

Returns true if the module is loaded and contains a public function with the given arity, otherwise false.

Examples:

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



回答2:


Use __ENV__

As I learned on another question, the __ENV__ macros gives access to various environment info, including __ENV__.functions and __ENV__.macros.

__ENV__.functions returns a list of tuples of modules and the list of functions they provide, like:

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

You could visually scan this for shoot, or write code to search through it.




回答3:


I am using Elixir 1.1.0 and what you are describing does not seem to be allowed. Here is the script (in 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

Now when I run iex aimable.ex, I get a CompileError

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

** (CompileError) aimable.ex:18: function shoot/0 imported from both Camera and Gun, call is ambiguous (elixir) src/elixir_dispatch.erl:111: :elixir_dispatch.expand_import/6 (elixir) src/elixir_dispatch.erl:82: :elixir_dispatch.dispatch_import/5




回答4:


Another potential technique (added for sake of completeness):

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

#etc.

or

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

# etc.

Then see which one won't compile correctly.

Just another way to accomplish this end.



来源:https://stackoverflow.com/questions/34325206/does-elixir-support-introspection-to-show-function-origins

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!