IEx tab completion for functions that impl a behaviour

I noticed today that I cannot do tab completion in iex to see which functions a module has if the function is implementing a behaviour. Why is that the case? Or, maybe something is messed up for me?

eg.

defmodule B do
  @callback hello() :: atom()
end

defmodule A do
  @behaviour B

  @impl B
  def hello do
    :world
  end
end

Then if I do iex -S mix and type “A.< tab >” I don’t see “hello” as I would expect.

EDIT: To clarify, if I paste the above code into a console, it does work, but not if I iex -S mix within a project that has the same modules defined.

What version of Elixir? I don’t have any issues with this on my machine.

iex(1)> defmodule B do
...(1)>   @callback hello() :: atom()
...(1)> end
{:module, B,
 <<70, 79, 82, 49, 0, 0, 4, 196, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 171,
   0, 0, 0, 16, 8, 69, 108, 105, 120, 105, 114, 46, 66, 8, 95, 95, 105, 110,
   102, 111, 95, 95, 10, 97, 116, 116, 114, ...>>, :ok}
iex(2)>
nil
iex(3)> defmodule A do
...(3)>   @behaviour B
...(3)>
...(3)>   @impl B
...(3)>   def hello do
...(3)>     :world
...(3)>   end
...(3)> end
{:module, A,
 <<70, 79, 82, 49, 0, 0, 4, 188, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 139,
   0, 0, 0, 15, 8, 69, 108, 105, 120, 105, 114, 46, 65, 8, 95, 95, 105, 110,
   102, 111, 95, 95, 10, 97, 116, 116, 114, ...>>, {:hello, 0}}
iex(4)> A.hello
hello/0

Well, at least I’m not going crazy.

Elixir 1.13.3 (compiled with Erlang/OTP 24)

For what it’s worth, it does work if I paste the modules directly into the console, but not if I load them via a mix project.

I guess according to the docs this is expected behavior elixir/iex.ex at main · elixir-lang/elixir · GitHub though I’d be interested to know why that decision was made.

1 Like

Oh, because behaviours are usually meant to satisfy a machine contract, not a human contract. The best example is GenServer. This is especially important because usually the public exports of a GenServer module are the external interface into the running server, whereas the behaviour implementations are meant to be called inside the process that wraps your server code.

In other words it basically never makes sense to call handle_* from IEX. The same logic applies, though less dramatically, for most other types of behaviours

2 Likes

Yeah, that makes sense to me about GenServer’s at least, but if that’s true then I’d be kind of surprised because it seems like the docs don’t really mention it only being a “behind the scenes” type of feature – like something you would only use when adding use SomeModule to a module.

2 Likes