Elixir’s Hidden Potions

Chris Gregori
Sep 1 · 5 min read

When I first started writing Elixir I fell for the expressive power of pattern matching, the refreshing first-class consideration of testing and documentation and the gorgeous syntax. It’s become my favourite language; and even though it has the most supportive community I have seen from a programming language, it still contains many exceedingly useful hidden talents. Over the years I’ve figured out a few tips and tricks that no one ever laid out for me, so I figured it was time I shared.

Photo by Jan Ranft on Unsplash

IEx

IEx is Elixir’s interactive shell — you can enter it by simply typing iex in your terminal of choice. Chances are you already knew that, but here are a few little bits you might not have known.

IEx doesn’t by default retain history between sessions — this means you can’t scroll back between what you might have been doing in previous sessions with your arrow keys or search through your history with Ctrl-r like you might expect you could.

Did you know you could turn that on?

You can either execute it on a per-shell basis:
iex --erl "-kernel shell_history enabled"

Or you can set it as an environment variable:
export ERL_AFLAGS="-kernel shell_history enabled" for MacOS
set ERL_AFLAGS "-kernel shell_history enabled" for Windows

Did you know about the built-in IEx helpers? Here are some of the most useful:

  • h/1 — prints help for given module or function
  • i/1 — prints information about the given term
  • v/0 — fetches last value from the history
  • v/1 — fetches nth value from history
  • recompile/0 — recompiles the current project]
  • c/1 - compiles a file
  • r/1 - recompiles the given module's source file

These are my favourites (and the ones I care to remember) but there are a fair few more to discover in the docs.

Have you ever been typing a multi-line expression in IEx, try and execute it and notice you missed a closing brace somewhere? Maybe a quotation mark?
If you hit Ctrl-c you’ll completely exit the shell — looks like you’re stuck my friend. Or are you?

#iex:break to the rescue. Simply type this on a line by itself in IEx and it will break out of any pending expression without ruining your entire terminal session and WIP Enum.reduce test.

Were you (or are you) a Java or Ruby developer and miss your breakpoints? IEx has you covered: IEx.pry/0 lets you set breakpoints in Elixir code and “pry” into running processes. This lets you have an IEx session running inside a given function.

Here’s an example of it being used:

def multiple(arg1, arg2) do
require IEx; IEx.pry()
arg1 * arg2
end

Testing

If you’re a fan of test-driven development then you’ll likely want to know about testing your stale files with mix.

The first time you run mix test --stale your entire test suite will run. This is because — technically — they’re all stale. On subsequent executions though only stale tests will be run.

So what is a stale test? A test is considered stale if it directly or indirectly references a module that has been changed (including the test file itself). If you want to run tests automatically anytime a file is changed then consider combining it with…

mix test.watch is an open-source library that automatically runs your Elixir projects tests every time you save a file.

Combine it with flags like --failed to really unlock your inner-TDD’er.

Another open-source library based on the aforementioned mix test.watch and Jest’s interactive watch mode which lest you dynamically change which tests should be run with minimal keystrokes.

Your tests will automatically run every a file changes — after your tests run you can use p test/my_project/test.exs or any pattern to change which tests are executed.

Check out the documentation for more in depth use.

Doctests are coding docs on steroids. Talked about extensively in the official language getting started guide but often missed out by many Elixir developers I’ve spoken to. Doctests let you annotate your functions with example usage (including expected output) and have them run as part of your test suite.

Taken from the getting started guide:

defmodule KVServer.Command do
@doc ~S"""
Parses the given `line` into a command.

## Examples

iex> KVServer.Command.parse("CREATE shopping\r\n")
{:ok, {:create, "shopping"}}

"""
def parse(_line) do
:not_implemented
end
end
# Running the tests...1) test doc at KVServer.Command.parse/1 (1) (KVServer.CommandTest)
test/kv_server/command_test.exs:3
Doctest failed
code: KVServer.Command.parse "CREATE shopping\r\n" === {:ok, {:create, "shopping"}}
lhs: :not_implemented
stacktrace:
lib/kv_server/command.ex:7: KVServer.Command (module)

Updating Data

If you’re writing Elixir, you’re going to be updating a Map structure sooner rather than later —and there are a fair few ways to do it; you could use Map.put/3 or you could use the built-in syntax:

my_data = %{
foo: 123,
bar: 456,
baz: 789
}
# Update my_datanew_data = %{ my_data | foo: 9999 }
# %{ foo: 9999, bar: 456, baz: 789 }

update_in/3 function takes a map as its first argument, a list of keys and a function that provides the newly updated value as its third. The built in syntax is useful for updating simple maps with a few values, but not if you need to update deeply nested data.

data = %{a: 1, b: 2, c: 3}update_in(data, [:b, :value], &(&1 * &1))# %{a: %{value: 1}, b: %{value: 4}, c: %{value: 3}}

update_in/3 is extremely useful but the rest of the Kernel is full of massively useful tools. Learn to love it.

And finally…

IO.inspect/2 is probably the function I use most when I’m developing Elixir and my absolute favourite thing about it, is that it returns the first argument it is given: meaning you can inject it in the middle of pipelines to take a look at exactly what’s happening:

[1, 2, 3]
|> IO.inspect(label: "Original")
|> Enum.map(&(&1 * &1))
|> IO.inspect(label: "Squared")

Join the team

Multiverse’s mission is to create a diverse group of future leaders. The products we are building are designed to connect apprenticeship candidates to employers and to set them up for success throughout their qualification. Our social mission is at the heart of our business, so if doing good is as important as building a commercial business to you, you will find Multiverse incredibly rewarding. You’ll also get a chance to put some of these great tips to use…

To view our latest open roles, visit our careers page. You could also check out Why you should join Multiverse by our awesome VP of product Emma van Dijkum or find out how Alex Drummond has been using Rust in Elixir for HTML validation.

multiverse-tech

Revolutionising the apprenticeship workforce with technology