Start Task.Supervisor With First Child

I want to start a Task.Supervisor with my application and put the first Task child in it. Any good ideas about how to structure this?

I’ve thought of having a line like this in the supervisor specification:

supervisor(Tricky, [])

Then defining the Tricky module as something like:

defmodule Tricky do
  def start_link do
    result = Task.Supervisor.start_link
    if is_tuple(result) and elem(result, 0) == :ok do
      elem(result, 1) |> Task.Supervisor.start_child(…)
    end
    result
  end
end

Thoughts?

2 Likes

I tend to reach for GenServers more quickly than Tasks and Agents. However, in a more tactical sense, is there something wrong with this?

{:ok, sup_pid} = result = Task.Supervisor.start_link
Task.Supervisor.start_child(sup_pid, ...)
result

I mean, if the supervisor doesn’t start correctly and you get an {:error, reason} result, does it really matter if you get a match exception here or not?

Edit more commentary: I find elem calls to be a cautionary code smell. Your code is rather defensive. Mine is more assertive in a “let it crash” sense, which is more idiomatic Elixir IMHO.

2 Likes

It might matter in some cases. The one that comes to mind is {:error, {:already_started, pid}}, which might be useful to a caller. So, I’d rather propagate the error in a standard way ({:error, reason}), than crash here:

with {:ok, sup_pid} <- Task.Supervisor.start_link(...) do
  do_something_with(sup_pid)
  {:ok, sup_pid}
end
4 Likes

Yes! with is perfect for this.

3 Likes