8 things to get you started with the Elixir's interactive shell (IEx)

Are you coming to Elixir from another language with an interactive shell? There are a few specific things about Elixir’s interactive shell (IEx) to keep an eye on and make ourselves more efficient. Here they are.

Mix environment

Running iex on its own is usually pretty limiting. To start the Elixir’s interactive shell for the Mix project at hand, we have to run it as iex -S mix in the project directory:

$ iex -S mix

Once it is started in this way, we can access both dependencies and the project code itself.

If we don’t have a Mix project or we need to run something outside of it, we can use the c/1 function together with the file path to load the code or module into memory:

iex(1)> c "file_with_functions.exs"

Since you cannot effectively manage Elixir dependencies outside Mix I almost always create a new Mix project by running mix new even if I want to write something very simple.

Command history

After running some code in IEx you might notice that there is no command history. This is indeed quite unfortunate because retyping everything is no fun at all.

Luckily we can enable IEx history by exporting Erlang’s -kernel shell_history enabled option via ERL_AFLAGS:

$ export ERL_AFLAGS="-kernel shell_history enabled"
$ iex

To have this always available we should put it to our shell profile file (such as ~/.bashrc for Bash):

export ERL_AFLAGS="-kernel shell_history enabled -kernel shell_history_file_bytes 1024000"

While we are at it we can also increase the memory for the historical data with -kernel shell_history_file_bytes option as shown above.

Module compilation

Since Elixir files are usually compiled (only .exs files do not get compiled), it’s necessary to recompile the module when we make some changes to its source.

Using the r/1 function we can recompile a specific module:

iex(1)> r MyApp.MyModule

There is also recompile, which simply recompile all changed modules in the current Mix project. This is super handy when we edit some source code and want to try the changes without running iex again.

iex(1)> recompile

Simply type recompile, hit up arrow (if history is enabled) followed by ENTER and we are running the new version of the code we were running before.

Documentation

We can easily access documentation for any available module by invoking the h/1 function:

iex(1)> h(Ecto.Repo)

                                   Ecto.Repo

Defines a repository.

A repository maps to an underlying data store, controlled by the adapter. For
example, Ecto ships with a Postgres adapter that stores data into a PostgreSQL
database.
...

There is also the open/1 function which can open the module source code in your desired editor (this needs to be set by using ELIXIR_EDITOR or EDITOR shell variable):

$ EDITOR=vi iex -S mix
iex(7)> open(Ecto.Repo)

Invoking the h/0 function without any argument shows documentation for IEx itself, including functions such as h/1 and open/1.

Debugging

Sometimes the documentation is not gonna cut it and we have to roll up our sleeves and get to debugging. With break! we can easily set up a breakpoint anywhere in the Mix project:

iex(1)> break!(MyModule.my_func/1)
iex(1)> break!(MyModule, :my_func, 1)

In case there is some recursion we can pass an additional argument for how many stops we want to make before stopping. There is also breaks/0 that lists all our breakpoints:

iex(1)> breaks()
 ID   Module.function/arity   Pending stops
---- ----------------------- ---------------
 1    MyModule.my_func/1        1

Once set we can call our module function and the execution will be stopped letting us inspect the environment:

iex(4)> MyModule.my_func("args")
Break reached: MyModule.my_func/1 (lib/my_module.ex:2)
    1: defmodule MyModule do
    2:   def my_func(args) do
    3:     "#{args}"
    4:   end
pry(1)> args
"args"

To finish and start a new shell process we can call respawn/0:

pry(2)> respawn

It will cleanly continue with execution as if we had never set up any breakpoints at all.

Inspecting failing tests

Although inspecting our functions is great it can be handy to inspect our failing tests.

We can drop IEx.pry in the failing test at hand:

# In test somewhere
require IEx; IEx.pry

and run iex -S mix test with the --trace option like:

$ iex -S mix test --trace path/to/simple_test.exs

Inspecting the test was never so convenient!

Custom configuration

If you need to reconfigure IEx a little you can create a .iex.exs file.

$ cat .iex.exs
alias Digi.Cache

defmodule H do
  def create_admin_roles do
    IO.inspect "Creating roles..."
    ...
  end
end

As seen above, this can be effectively used to define aliases (We all have long module names somewhere, don’t we?) or simply handy modules that can help us with something. .iex.exs can be globally located in user home (~/.iex.exs) or local in the project root directory.

Here is a bit more on the custom configuration.

Printing integer lists

You might have been bitten before by the following behavior:

iex(1)> [27, 35, 51]
'\e#3'

This is the explanation from Elixir FAQ:

Pretty-printing of lists is done by using Erlang’s native function. It is designed to print lists as strings when all elements of the list are valid ASCII codes.

Solution? Either add 0 to the list (works directly, with inspect/1 and IO.inspect/1):

iex(2)> [27, 35, 51] ++ [0]
[27, 35, 51, 0]

Or use inspect/1’s charlists: :as_lists option:

iex(3)> inspect [27, 35, 51], charlists: :as_lists
"[27,35,51]"
Any comments? Write me a DM on Twitter.

Before you leave…

I am writing an introductory book on web application deployment. Networking, processes, systemd, backups, and all your usual suspects.

Check it out →