Reputation: 126072
If a module import
s 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
?
Upvotes: 6
Views: 734
Reputation: 126072
__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.
Upvotes: 3
Reputation: 15343
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.
Upvotes: 0
Reputation: 9109
You can do this directly:
# from inside the module; IO.inspect(&Aimable.shoot/0) reveals nothing
IO.inspect &shoot/0 #=> &Gun.shoot/0
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
Upvotes: 6
Reputation: 7779
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
Upvotes: 1