Fl4m3Ph03n1x

Fl4m3Ph03n1x

Elixir in Action Application structure is wrong

Background

I have a project that I have recently converted to run as an OTP application. Upon doing this I came back to the “Elixir in Action” book and replay the same steps - but I found something is wrong in the book.

Code

This is how @sasajuric defines an Application (I have added typespec for calrity):

defmodule Todo.Application do
  use Application
  
  @impl Application
  @spec start(any, any) :: Supervisor.on_start
  def start(_, _) do
    Todo.System.start_link()
  end
end

Simple enough. Todo.System is a simple supervisor:

defmodule Todo.System do

  @spec start_link() :: Supervisor.on_start
  def start_link do
    Supervisor.start_link([...children...], strategy: :one_for_one)
  end
end

Problem

At first glance it seems like everything is as it should be. But dialyzer pointed me a problem:

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(undefined)

This all means that what Todo.System.start_link returns (Supervisor.on_start which expands to something similar to{:ok, pid} | :ignore | {:error, any}) is not compatible with what Todo.Application.start should return: :ok | {:error, term}.

Now, I am not sure what consequences this can have in the long term. The apps launch for now, but this may change on latter updates.

Am I missing something?

But maybe I am missing something? Maybe dialyzer is not smart enough to see some hidden compatibility going on and I am just crying wolf for nothing. What do you think? Is my analysis correct?

On the other hand, if this is in fact wrong, it would be nice if the author could issue a fix (perhaps in the 3rd edition :stuck_out_tongue: )

Marked As Solved

sasajuric

sasajuric

Author of Elixir In Action

I personally wouldn’t bother. Returning :ignore from the top-level supervisor is a bug, and so it’s not something I wouldn’t deal with in the code, since either way the app won’t start, and the original error message is IMO clear enough.

Also Liked

sasajuric

sasajuric

Author of Elixir In Action

This is not how I defined it, the typespec is your own addition :slight_smile:

You are :slight_smile: The error message states that ignore return type of the specification is not the subtype of the expected return type for the callback of the application behaviour.

Wher does ignore come from? It’s a part of Supervisor.on_start type.

The Application.start spec in contrast states that valid return values are {:ok, pid()} | {:ok, pid(), state()} | {:error, reason :: term()} (notice the lack of :ignore).

So basically the typespec you provided for Todo.Application.start is incorrect. You could use something like @spec start(Application.start_type, term) :: {:ok, pid} instead.

Fl4m3Ph03n1x

Fl4m3Ph03n1x

On a personal level, the main reason I recommend your book to people around me in work (besides liking it obviously) is the fact I you are so active in this community.

If one of my co workers has a question about your book that I can’t answer, they can always ask the creator for help. And that is priceless.

sasajuric

sasajuric

Author of Elixir In Action

Happy to hear that!

I think what’s even more important is that we have a nice community here, so different people can chime in with different points of view, like e.g. @LostKobrakai did earlier in this thread by spotting the difference between Application.start and the start callback. This is why I usually advise people to ask their questions on this forum.

LostKobrakai

LostKobrakai

https://hexdocs.pm/elixir/Application.html#start/2
is not the same as
https://hexdocs.pm/elixir/Application.html#c:start/2

So the expected return type of Todo.Application.start/2 should be {:ok, pid()} | {:ok, pid(), state()} | {:error, reason :: term()}, but you’re correct that :ignore is not allowed as return value for the application.

sasajuric

sasajuric

Author of Elixir In Action

Your post asks a perfectly valid question, no need to be hard on yourself.

I’ll keep it in mind for the next edition, thanks!

As the author, I feel I should point out that EiA isn’t a reference book, and so it doesn’t treat every nuance of the language. Even the aspects it talks about aren’t covered in full depth. There’s simply no room for that, but more importantly, I feel that I would lose most of the readers if I went to fine-print detail of every single topic I’m discussing. Thus, some nuances will inevitably be left out, so don’t be surprised if you will have further questions.

Where Next?

Popular in Questions Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement