BoardClic

Tech blog

Tips & tricks for IEx - Elixir's interactive shell

Published 6 May 2022

When building an application with Elixir and Phoenix you inevitably spend a lot of time with IEx, Elixir’s wonderful interactive shell, testing things out.

Maybe you quickly need to update or delete a database record? Maybe you need to test out a pipeline of functions you have? Or just come up with the best way to transform that list of data you have. Either way having IEx at disposal is extremely helpful.

We will, in this article, list a couple of tips and tricks that we have picked up along the way while using IEx. Some of these might already be familiar to you, but maybe some are new to you and can help you get more productive and/or ease your day.

Autoload aliases

While inside the IEx maybe you need to use a function that lives in a module with a very long and nested name? Let’s see an example of how that could look like. Imagine that we have are building an application for recipes. A recipe likely contains ingredients but also steps to instruct how to follow the recipe.

The module name for the ingredients would likely be something like MyPrettyLongAppName.Recipes.Ingredient and for steps MyPrettyLongAppName.Recipes.Step. Now imagine that you want to see if a step contains the data you imagine in the IEx shell, you would do something like this:

  iex(1)> MyPrettyLongAppName.Recipes.Step |> MyPrettyLongAppName.Repo.get("some-unique-id")

That would be very cumbersome to write if you need to do this every now and then. So maybe you are prepared and know that you need to do this more than once today and you make an alias in the current IEx session so that you can access the module without having to write the full name:

iex(1)> alias MyPrettyLongAppName.Recipes.Step
iex(2)> alias MyPrettyLongAppName.Repo
iex(3)> Step |> Repo.get("some-unique-id")

But wouldn’t it be nice to have the modules that you occasionally have to use in IEx always aliased? Well you can! In the root of your project create a .iex.exs file and just put your aliases in there! Note the dot in the filename. You can of course do more than just put aliases in there, for example require or import some modules and even bind variables that you can then access from the shell.

Now when you start your project with iex -S mix or iex -S mix phx.server all your aliases and imports are ready to be used! You can read more in the iex.exs documentation.

Break out of expression

From time to time you inevitably get trapped in an unfinished expression inside the interactive shell without it being obvious what character that’s missing to complete the expression. Here’s a nasty, but probably not that unrealistic example:

  iex(1)> ["ab
  iex(1)> ]
  iex(1)>
  iex(1)> "
  iex(1)> "
  iex(1)> ]

See how we are not able to get out of the expression? Instead of hitting CTRL + C twice and start over we can use #iex:break to simply break our unfinished expression:

  iex(1)> ["ab
  iex(1)> ]
  iex(1)>
  iex(1)> "
  iex(1)> "
  iex(1)> ]
  iex(1)> #iex:break
  ** (TokenMissingError) iex:2: incomplete expression

  iex(1)> "Phew, I'm free!"
  "Phew, I'm free!"
  iex(2)>

And now you can retry writing the expression or continue with something else with all your history still intact!

Bind or use last expression

Are you maybe coming to Elixir from the Ruby on Rails world? Then you’ve likely done something like this to bind the last expression to a variable in the Rails console:

  irb> [1, 2, 3, 4, 5]
  => [1, 2, 3, 4, 5]
  irb> list = _
  irb> list
  => [1, 2, 3, 4, 5]

To do the same thing in the IEx shell you would do this:

  iex(1)> ["Elixir", "Phoenix", "Liveview", "Ecto"]
  ["Elixir", "Phoenix", "Liveview", "Ecto"]
  iex(2)> list = v()
  ["Elixir", "Phoenix", "Liveview", "Ecto"]
  iex(3)> list
  ["Elixir", "Phoenix", "Liveview", "Ecto"]

The v(n \\ -1) function returns the value of the nth expression. As you can see the default argument for n is -1, which means if we don’t send it any arguments like in the example above it will return the last expression. That means if we press Enter inbetween the expression and when we want to bind it to a variable we have to do this:

  iex(1)> "I want to bind this to a variable!"
  "I want to bind this to a variable!"
  iex(2)>
  nil
  iex(3)> my_string = v(-2)
  "I want to bind this to a variable!"
  iex(4)> my_string
  "I want to bind this to a variable!"

You can even continue a pipeline from the last expression:

  iex(1)> "It's so much fun working with Elixir!"
  "It's so much fun working with Elixir!"
  iex(2)> |> String.upcase()
  "IT'S SO MUCH FUN WORKING WITH ELIXIR!"
  iex(3)> |> String.split()
  ["IT'S", "SO", "MUCH", "FUN", "WORKING", "WITH", "ELIXIR!"]

In the background it does this:

  iex(1)> "It's so much fun working with Elixir!"
  "It's so much fun working with Elixir!"
  iex(2)> v() |> String.upcase()
  "IT'S SO MUCH FUN WORKING WITH ELIXIR!"

Bonus - shell history

A really annoying thing is to lose your history when exiting the IEx shell. You can add history by starting the IEx-shell with iex --erl "-kernel shell_history enabled". But more likely you would want to have this every time without having to explicitly write it. You can do that by adding it to your shell configuration. For a Unix system you would add the below snippet to your .bashrc/.zshrc file. For other systems take a look in the IEx documentation.

  export ERL_AFLAGS="-kernel shell_history enabled"

Conclusion

Hopefully some of these tips were unknowed to you and can now help you in your day to day life, no matter if you use Elixir in your workplace or just playing around with it in your free time (and day dreaming about using it at your workplace).

Either way, thank your for taking the time to read this article! Be sure to come back later for a tips and tricks #2!

Rickard Jonsson
Rickard Jonsson