paseg
@behaviour, @callback and @spec
Hi
When I use @behaviour and @callback, the functions are defined. I guess that I do not need to use @spec for the implementation of the @impl functions as well?
Will Dialyzer sort this out as well?
Br Patrik
Most Liked
asummers
You don’t need them as Dialyzer will give a callback does not match spec error, but please include them anyway. As a reader I do not want to have to jump to the behaviour definition to find out what the arguments are. In that vein if you include a using macro where you define default callbacks that are overridable with defoverridable, elide the spec in the using macro, otherwise you’ll get a compiler error for duplicating the spec if anyone actually overrides and wants to spec the override.
YES
defmodule MyBehaviour do
@callback foo() :: :ok
end
defmodule MyImpl do
@impl MyBehaviour
@spec foo() :: :ok
def foo(), do: :ok
end
YES
defmodule MyBehaviour do
@callback foo() :: :ok
defmacro __using__(_) do
quote do
@impl MyBehaviour
@spec foo() :: :ok
def foo(), do: :ok
end
end
end
defmodule MyImpl do
use MyBehaviour
end
YES
defmodule MyBehaviour do
@callback foo() :: :ok
defmacro __using__(_) do
quote do
@impl MyBehaviour
def foo(), do: :ok
defoverridable [foo: 0]
end
end
end
defmodule MyImpl do
use MyBehaviour
@impl MyBehaviour
@spec foo() :: :ok
def foo(), do: :ok
end
NO
defmodule MyBehaviour do
@callback foo() :: :ok
defmacro __using__(_) do
quote do
@impl MyBehaviour
@spec foo() :: :ok
def foo(), do: :ok
defoverridable [foo: 0]
end
end
end
defmodule MyImpl do
use MyBehaviour
# this errors if you include the spec
# @spec foo() :: :ok
@impl MyBehaviour
def foo(), do: :ok
end
paseg
Hi
Wow, thanks for all comments! Did not know that this would stir up this many opinions. ![]()
A see your point @asummers, but since this is not a public library (“only” used within our company), I prefer that the implementers spend the extra time to go into the definition of the behaviour rather than using multiple specs that will effect the maintenance in the long run.
I also found that the @spec may state less than the actual @callback without Dialyzer telling me, witch gives me another argument not to use the @specs…
Example given:
defmodule Register.DocEvents do
@callback initialize(soure :: binary() | atom()) :: :ok | {:error, String.t()}
end
defmodule Register do
@behaviour Register.DocEvents
@impl Register.DocEvents
@spec initialize(atom()) :: :ok
def initialize(source) do
...
end
end
Dialyzer signals this is ok, and I guess it is since the actual implementation fits within the original specification. In this case, the @spec makes sense since this implementation is not the same as the @callback stated, but if they are expected to be the same then adding an extra @spec just creates more maintenance burden.
Eiji
Sure, here is your changed code:
defmodule MyBehaviour do
@callback foo() :: :ok
end
defmodule MyImpl do
@behaviour MyBehaviour
@doc delegate_to: {MyBehaviour, :foo, 0}
@doc "Implementation-specific docs goes here …"
@impl MyBehaviour
def foo(), do: :ok
end
which would give:
iex(1)> h MyImpl.foo
def foo()
delegate_to: MyBehaviour.foo/0
Implementation-specific docs goes here …
iex(2)> b MyBehaviour.foo/0
@callback foo() :: :ok
This is much simpler than writing macros or copy-paste documentation and spec.
Generally we should avoid using macros unless it’s required.
Last Post!
yatender-oktalk
Thank you for the discussion everyone!
it helped me really to read the different perspectives and make decisions.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex










