[001.3] Mix and Modules

Mix and Modules
[02.13.2017]

One of the great things about Elixir is its fantastic tooling support out of the
box. The gateway to this tooling is mix, which is a bit like make or rake.
Let's get started

Project

We'll start off by looking at the help:

mix help

There's a lot to take in there, but here you're essentially seeing a list of all
of the tasks that mix knows about. You can find out more about a particular
task. Since we're going to create a new project, let's look at the help for mix
new:

mix help new

mix new
Creates a new Elixir project. It expects the path of the project as argument.
mix new PATH [--sup] [--module MODULE] [--app APP] [--umbrella]
A project at the given PATH will be created. The application name and module
name will be retrieved from the path, unless --module or --app is given.
A --sup option can be given to generate an OTP application skeleton including a
supervision tree. Normally an app is generated without a supervisor and without
the app callback.
An --umbrella option can be given to generate an umbrella project.
An --app option can be given in order to name the OTP application for the
project.
A --module option can be given in order to name the modules in the generated
code skeleton.
## Examples
mix new hello_world
Is equivalent to:
mix new hello_world --module HelloWorld
To generate an app with a supervision tree and an application callback:
mix new hello_world --sup
Location: /Users/jadams/.asdf/installs/elixir/1.4.0/lib/mix/ebin

We don't really need any of the options to start our first project:

mix new hello_world

Now we have a new project. We can cd into it and run the tests, though there
won't really be any useful ones:

Next, there's a _build directory where build artifacts live. You can see
beam modules in there, which are what your code gets compiled down to in order
to run on the Erlang VM. These won't exist until you compile your code, which
happened when we ran the tests.

Next, we see a config directory. You'll use this to configure your apps, but
we won't look at it too closely yet.

There there's the lib directory. Your modules live here, and you can see that
we had one auto-generated by mix.

Then there's mix.exs which is the file that describes our mix project
entirely.

Finally, you can see the tests.

Let's look at the lib/hello_world.ex file to check out our HelloWorld
module:

What just happened here? Well, we used pattern matching to describe our function
in the case that the second argument was strictly zero. You can define multiple
function heads to define variants of the same function.

Let's change this a bit so we can pattern match on whether or not the function
was successful:

defmoduleHelloWorlddodefdiv(a,0)do{:error,"attempt at division by zero"}enddefdiv(a,b)do{:ok,a/b}endend

Now we can handle this result in a case statement. Let's do it in a test:

vim test/hello_world_test.exs

defmoduleHelloWorldTestdouseExUnit.CasedoctestHelloWorldtest"division"do{:ok,result}=HelloWorld.div(2,1)assertresult==2.0endtest"division by zero"do{:error,err}=HelloWorld.div(1,0)asserterr=="attempt at division by zero"endend

So that's covered the basics of testing, functions and modules. Let's look at
one more trick Elixir has up its sleeves: Pipes.

When you use a pipe, which looks like |>, you're really just moving the
first argument to a function out of the function call, to the left of the pipe.
We'll open an iex session to check it out:

iex -S mix

This:

iex(1)> HelloWorld.div(1, 2)
{:ok, 0.5}

is the same as this:

iex(2)> 1 |> HelloWorld.div(2)
{:ok, 0.5}

That's piping in essence, though it's not amazing when used for a single case.
Let's open up our test and see it in action on a longer pipeline:

vim test/hello_world_test.exs

defmoduleHelloWorldTestdo# ...test"pipes and strings"do# If we import the `String` module we can use its functions without# qualifying them fully - so we get `upcase` instead of the more-verbose# `String.upcase`importStringval="josh"|>reverse|>capitalize|>reverseassertval=="josH"endend

In longer pipelines, it becomes a lot more obvious where the benefit lies. Try
to write the same code without pipes - just with nested function calls - and see
if you think it's easier to see what's going on. I think you won't.

Summary

In today's episode we saw how to use mix and we saw how to create and test
modules. We also saw a use of a 2-tuple return value, how to define a function
with multiple function heads, and how to use pipelines. I hope you enjoyed it.
See you soon!

sign up for full access

Meet your expert

I've been building web-based software for businesses for over 18 years. In the last four years I realized that functional programming was in fact amazing, and have been pretty eager since then to help people build software better.