TIL: 3 different ways to build a list with conditional elements in Elixir (and what that has to do with application startup)

6 minutes reading time
  • Christoph Grothaus
    Christoph Grothaus

Sometimes you are faced with the task to build a list out of elements where some of the elements are conditional. The Dart programming language even has a special syntax for this (collection if). In my case, I am using the Elixir language and the Phoenix web framework (but the latter is not specific to the solution, it's just needed for my example). Let's see how we can build a list with conditional elements there.

Background: the example

Let’s use the Application   module in a standard phoenix app as an example. It contains a list of child processes to be started with the application like this:

children = [
  # Start the Ecto repository
  # Start the Telemetry supervisor
  # Start the PubSub system
  {Phoenix.PubSub, name: PhoenixDemo.PubSub},
  # Start the Endpoint (http/https)
  # Start a worker by calling: PhoenixDemo.Worker.start_link(arg)
  # {PhoenixDemo.Worker, arg}

This list is unconditional, all children are always started. After adding some features, I needed to start some children only in some cases, like a task scheduler or an OpenID Connect worker process, which might not be necessary for local development.

First, I attempted to take the standard children list and conditionally concatenate the other children to it. But the order of list elements determines the startup and shutdown order, and the Endpoint entry has to come last, so I needed several sublists. This approach was cumbersome. Finally I came up with a list of possible children, where each is a tuple of {boolean, child_spec}:

possible_children = [
  # Start the Ecto repository
  {true, PhoenixDemo.Repo},
  # Start the Telemetry supervisor
  {true, PhoenixDemoWeb.Telemetry},
  # Start the PubSub system
  {true, {Phoenix.PubSub, name: PhoenixDemo.PubSub}},
  # Start the OpenID Connect Worker for Ueberauth
    {OpenIDConnect.Worker, Application.get_env(:ueberauth, Ueberauth.Strategy.OIDC)}},
  # Start the Scheduler
  {start_scheduler?(), DigitalServicesPortal.Scheduler},
  # Start the Endpoint (http/https)
  {true, PhoenixDemoWeb.Endpoint}
  # Start a worker by calling: PhoenixDemo.Worker.start_link(arg)
  # {PhoenixDemo.Worker, arg}

That way I can easily specify all child processes in the correct order. The ones that must always be started have a hardcoded true as their first tuple element, the ones that are conditional use private helper functions like start_scheduler? (not shown here) to determine whether they should start.

Now, all I need to do is to transform this list into the final list of children: filter all list elements where the first tuple element is true, and then map all list elements to the second tuple element.


There are several ways to achieve this goal. If Elixir just had a filter_map function, like Ruby has! It turns out that at some point in time Elixir did indeed have such a function, but it got deprecated with Elixir version 1.5, in favor of combining other existing functions.

1. Chaining Enum.filter and

This one uses filter/2 and map/2 in a straightforward manner:

children =
  |> Enum.filter(fn tuple -> elem(tuple, 0) end)
  |> tuple -> elem(tuple, 1) end)

I always feel a little uncomfortable reaching into tuples with elem/2. This solution can also be written in another way by pattern matching on the tuple:

children =
  |> Enum.filter(fn {start?, _} -> start? end)
  |> {_, child} -> child end)

And, just to be complete about this, it can also be written with Stream.filter and

2. Using Enum.flat_map and pattern matching on the anonymous function arguments

This one is an unconventional usage of flat_map/2 with a multi-clause pattern match on the anonymous function arguments:

children =
  |> Enum.flat_map(fn
    {true, child} -> [child]
    {false, _} -> []

3. Using a comprehension

This one relies on a comprehension with a filter:

children = for {start?, child} <- possible_children, start?, do: child

It turns out this can be written even shorter, as the left hand side of the comprehension, the so-called generator expression, supports pattern matching and all non-matching patterns are ignored:

children = for {true, child} <- possible_children, do: child


This was really fun, trying out different ways to build a list with conditional elements in Elixir! Personally, I like option 3 best, it is concise and elegant, and besides it neatly solves my application startup problem.

You are looking for the right partner for successful projects?
We'd love to help – let's get in touch!
Contact us