It seems like when you define a behaviour the compiler requires that you include type specs within the @callback
definition. Then when you adopt the behaviour, the compiler warns if function_name/arity
is not defined, but is perfectly happy if you don’t follow the type specs.
My question is, why does the compiler enforce that @callback
definitions include type specs? I understand that Dialyzer will check these type specs for you, but that doesn’t seem like something the compiler should care about. Also, this means that anyone who wants to use behaviours to get compile-time function_name/arity
checks has to buy-in to writing type specs, even if they otherwise want to use Elixir as a purely dynamic language.
For example:
If we omit the type specs, we get a compile error
defmodule Greeting do
@callback hello(person)
end
# (CompileError) iex:82: type specification missing return type: hello(person)
To make the compiler happy, we have to include type specs:
defmodule Greeting do
@callback hello(%Person{}) :: {:ok, String.t} | {:error, String.t}
end
Now when we adopt the behaviour, the compiler checks that function_name/arity
is defined:
defmodule WesternGreeting do
@behaviour Greeting
def hello(), do: "Howdy"
end
# warning: undefined behaviour function hello/1 (for behaviour Greeting)
However all the type specs in the @callback are disregarded by the compiler:
defmodule WesternGreeting2 do
@behaviour Greeting
def hello([a, b, c]), do: a <> b <> c
end
# No warnings or errors