Background
I am trying to add specs to all my public functions, but dialyzer gives me warnings on Supervisor.start_link/2
that according to the docs should not happen.
Code and Error
@spec start(any, any) :: Supervisor.on_start
def start(_type, _args) do
children = [ ]
opts = [strategy: :one_for_one, name: IslandsEngine.Supervisor]
Supervisor.start_link(children, opts)
end
lib/islands_engine/application.ex:6: The return type ‘ignore’ in the specification of start/2 is not a subtype of {‘error’,} | {‘ok’,pid()} | {‘ok’,pid(),}, which is the expected return type for the callback of the ‘Elixir.Application’ behaviour
Documentation
The documentation for Supervisor.start_link/2 clearly specifies there is an :ignore
type:
https://hexdocs.pm/elixir/Supervisor.html#t:on_start/0
Question
What am I doing wrong?
This links to the function Application.start/2
not the callback: https://hexdocs.pm/elixir/Application.html#c:start/2
@Fl4m3Ph03n1x
Supervisor.on_start
includes :ignore
, which is not allowed for the callback of the Application behaviour.
Could you elaborate?
In Elixir the return of a function is the return of its last statement. Since Supervisor.start_link
returns what I have shown above, it stands to reason that start
will do it to.
Unless you are telling me that my start
applications should not terminate with Supervisor.start_link
, I don’t know how else to solve this.
Supervisor.start_link
can return :ignore
, but a supervisor never does so without you implementing it to do that. So unless you’re purposefully starting a root supervisor, which can return :ignore
in its init/1
callback this is totally fine. If you really want to you can wrap it in a pattern match, which only allows {:ok, pid} | {:error, term}
to actually return. This will crash the Application just as much as currently if Supervisor.start_link
returns :ignore
, but you crash because of the pattern match and not because of an invalid return value.
Supervisor.start_link
can return :ignore
, but a supervisor never does so without you implementing it to do that.
So, the documentation is wrong?
Should I create a ticket or something to fix it?
It’s not really wrong. There are cases where the function can return :ignore
, but they don’t happen randomly. If it returns :ignore
that means a developer set the supervisor up to do so by making the init/1
callback return :ignore
.
From a quick glance at the code it seems Supervisor.start_link/2
actually really cannot return :ignore
at the moment as there seems to be no path to bring a :ignore
to the supervisor module it’s using behind the scenes. (There’s not much reason for it to be honest as you simply can just not call start_link/2
as well at this point.)
Supervisor.start_link/3
on the other hand can return :ignore
for sure, as this one works with custom supervisor modules the user implements.
To bring this back to your case. Unless you use a supervisor which some developer setup to be able to return :ignore
this will match up with the expected return values of the Application.start/2
callback. If a developer does so this will fail as well, as you’re not starting a proper root supervisor for the app to start. A app, which cannot start brings down the whole vm anyways.
1 Like
So I can’t get rid of the dialyzer error, correct?
The only way I see is to remove the @spec
so it wont complain.
At this point I have the idea my code is correct and doesn’t go against any community guidelines, but I still have a tool that complains about it, which is distressing.
So you know Supervisor.start_link
won’t return :ignore
in this specific case. So the return value is only {:ok, pid()} | {:error, {:already_started, pid()} | {:shutdown, term()} | term()}
. Using those should make make dialyzer happy, as it aligns with what Application.start
expects.
3 Likes