Elixir School
Presents

TIL about `IO.inspect/2`'s `:label` opt

By Sean Callan

Did you know you could label your output? Neither did we! Check out today's TIL to learn more.

If you’ve ever found yourself debugging Elixir then you’re probably familiar with IO.inspect/2 but just in case let’s see an example of how we might use it:

defmodule Example do
  def sanitize_params(params) do
    params
    |> IO.inspect()
    |> Map.take(["quantity", "price"])
    |> IO.inspect()
    |> Enum.into(%{}, fn {k, v} -> {k, String.to_integer(v)} end)
  end
end

Our function is simple: Given a map, take some keys, and cast them to integers; for this example we won’t worry about invalid inputs and error handling.

Let’s see the output in IEx:

iex> params = %{"price" => "100", "quantity" => "1", "onsale" => true}
iex> Example.sanitize_params(params)
%{"onsale" => true, "price" => "100", "quantity" => "1"}
%{"price" => "100", "quantity" => "1"}
%{"price" => 100, "quantity" => 1}

Looking at the code and output side-by-side, we can pretty easily follow along but without that we have to remember what and where we put our IO.inspect/2 calls for the output to be meaningful. Can you imagine situations where there’s a lot more output on the screen and the code isn’t as simple?

Allow me to introduce you to our new friend the :label option! Let’s revisit our previous code, introduce the ever helpful :label option, and jump straight into the IEx output:

defmodule Example do
  def sanitize_params(params) do
    params
    |> IO.inspect(label: "input")
    |> Map.take(["quantity", "price"])
    |> IO.inspect(label: "Map.take/2")
    |> Enum.into(%{}, fn {k, v} -> {k, String.to_integer(v)} end)
  end
end
iex> params = %{"price" => "100", "quantity" => "1", "onsale" => true}
iex> Example.sanitize_params(params)
input: %{"onsale" => true, "price" => "100", "quantity" => "1"}
Map.take/2: %{"price" => "100", "quantity" => "1"}
%{"price" => 100, "quantity" => 1}

Whoa! Our debugging output now has labels that make tracing our code so much easier. It gets better too! Labels don’t have to be preset, we can use our captured values for dynamic labels:

iex> Enum.each(%{"a" => 1, "b" => 2, "c" => 3}, fn {k, v} -> IO.inspect(v, label: k) end)
a: 1
b: 2
c: 3

How cool is that?!

Did you know about :label before? If so, have you found it as useful as we have?