This chapter will cover the following recipes:
Interactive Elixir (IEx):
Using the terminal to prototype and test ideas
Loading and compiling modules
Getting help and accessing documentation within IEx
Using Erlang from Elixir
Inspecting your system in IEx
Inspecting your system with Observer
Mix:
Creating a simple application
Managing dependencies
Generating a supervised application
Generating umbrella applications
Managing application configuration
Creating custom Mix tasks
The command line is the preferred way to create and interact with Elixir applications, inspect running systems, and prototype ideas.
Interactive Elixir (IEx) allows immediate evaluation of any expression, and it is also possible to define modules directly without saving them previously on a file. Similar tools exist in other programming languages; Ruby's IRB or Clojure's REPL are some examples.
Mix is a build tool that provides several tasks to create, compile, and test projects, and handle dependencies. It is also possible to define custom tasks with Mix. In the Creating custom Mix tasks recipe, we will create a task to display the memory usage. It is common for some applications to define their own tasks. Phoenix framework (which will be covered in Chapter 7, Cowboy and Phoenix) is just one example of this.
The Elixir default installation provides an REPL (short for read-eval-print-loop) named IEx. IEx is a programming environment that takes user expressions, evaluates them, and returns the result to the user. This allows the user to test code and even create entire modules, without having to compile a source file.
To start prototyping or testing some ideas, all we need to do is use IEx via our command line.
To get started, we need to have Elixir installed. Instructions on how to install Elixir can be found at http://elixir-lang.org/install.html. This page covers installation on OSX, Unix and Unix-like systems, and Windows. It also gives some instructions on how to install Erlang, which is the only prerequisite to run Elixir.
To prototype and test the ideas using IEx, follow these steps:
Start IEx by typing
iex
in your command line.Type some expressions and have them evaluated:
iex(1)> a = 2 + 2 4 iex(2)> b = a * a 16 iex(3)> a + b 20 iex(4)>
Define an anonymous function to add two numbers:
iex(5)> sum = fn(a, b) -> a + b end Function<12.90072148/2 in :erl_eval.expr/5>
Invoke the function to add two numbers:
iex(6)> sum.(1,2) 3
Quit from IEx by pressing Ctrl + C twice.
IEx evaluates expressions as they are typed, allowing us to get instant feedback. This allows and encourages experimenting ideas without the overhead of editing a source file and compiling it in order to see any changes made.
Note
In this recipe, we used the =
operator. Unlike other languages, =
is not an assignment operator but a pattern matching operator. We will get into more detail on pattern matching in the Using pattern matching and Pattern matching an HTTPoison response recipes in Chapter 2, Data Types and Structures.
In step 3, we used a dot (.
) in the sum
function right before the arguments, like this: sum.(1,2)
. The dot is used to call the anonymous function.
It is possible to load code from source files into an IEx session. Multiple modules may be loaded and used, allowing us to incorporate existing code into our prototyping or idea testing session.
In this recipe, we will be importing two files that define the Greeter
and Echoer
modules into our IEx session.
In the following lines, we will list the contents of these modules:
code\greeter.ex defmodule Greeter do def greet(name \\ "you") do "Hello #{name} !" end end code/echoer.ex defmodule Echoer do def echo(msg) do IO.puts "#{msg} ... #{msg} ...... #{msg}" end end
Tip
Downloading the example code
You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
We will follow these steps to load and compile the modules:
Start IEx:
iex
Load the
Greeter
module defined ingreeter.ex
:iex(1)> c("greeter.ex") [Greeter]
Load the
Echoer
module defined inechoer.ex
:iex(2)> c("echoer.ex") [Echoer]
Use the
greet
function defined in theGreeter
module:iex(3)> Greeter.greet("Me") "Hello Me !"
Use the
echo
function defined in theEchoer
module:iex(4)> Echoer.echo("hello") hello ... hello ...... hello :ok
Combine the functions defined in both modules:
iex(7)> Greeter.greet("Me") |> Echoer.echo Hello Me ! ... Hello Me ! ...... Hello Me ! :ok
When c("file_name.ex")
is invoked from IEx, the file is loaded and compiled (a corresponding file with the .beam
extension will be created).
The module (or modules) defined on each imported file become available. It is possible to invoke functions on these modules using the ModuleName.function_name(args)
syntax.
If a module_name.beam
file exists for a given module, then every time you import that module into an IEx session, you will see the following warning:
module_name.ex:1: warning: redefining module ModuleName
The warning means that a new compiled .beam
file is being created, potentially redefining the module. If no changes were made to the source code, the code will be the same, although the warning is still issued.
In step 6, the pipe operator (|>
) is used to simplify the code. This operator means that the output of the left operation will be fed as the first argument to the right operation.
This is equivalent to writing the following:
Echoer.echo(Greeter.greet("Me"))
In steps 2 and 3, the greeter.ex
and echoer.ex
files are imported without indicating the path because they are under the same directory from where the IEx session was started.
It is possible to use relative or full paths when loading files:
Documentation is a first-class citizen in the Elixir ecosystem, so it comes as no surprise that IEx provides convenient ways to access documentation and get help without the need to leave an IEx session.
This recipe exemplifies the use of the defined help functions.
We will follow these steps to get help and access documentation in an IEx session:
Enter
h
inside a running IEx session to see the help options related to the use of IEx helpers, as shown in this screenshot:If we wish, for instance, to get information regarding the
c/2
function, we typeh(c/2)
, as shown in the following screenshot:Accessing a module documentation is done by invoking
h(ModuleName)
. In the next screenshot, we access documentation related toEnum
:Getting information about a specific function inside a module is also possible by invoking
h(ModuleName.function_name)
. The following screenshot shows the documentation forEnum.map
:
When we define modules, it is possible to use the @moduledoc
and @doc
annotations to define documentation related to the whole module or to a specific function in that module.
IEx parses the documentation defined with these annotations and makes it available in a convenient way so that there's no need to leave the session when help or some more information is needed.
IEx itself has several helper functions defined (refer to the first screenshot of this recipe), and among them, we find h/0
and h/1
.
There are several defined functions that allow accessing information on function specifications and types (if defined). To learn more, you can use s/1
and t/1
.
As an example, to get information on the types defined for the Enum
module, we would use t(Enum)
, and to get information on the specifications, we would use s(Enum)
.
Elixir code runs in the Erlang VM. The ability to invoke Erlang code from within Elixir allows the use of resources from the entire Erlang ecosystem, and since Elixir code is compiled to the same byte code as Erlang code, there is no performance penalty.
It is also possible to include in an Elixir application the Erlang code that is already compiled.
If you take a closer look, the files we compile in IEx sessions have the .beam
extension, and that's exactly the same format Erlang's compiled code gets transformed into.
These are some examples of how to invoke Erlang code from Elixir:
Erlang's
Application
module defines a function namedwhich_applications
. This function returns a list of applications being executed in an Erlang VM. This is the way to use this function from Elixir:iex(1)> :application.which_applications
To get information on any Erlang module, there is a function named
module_info
. To know more about theerlang
module, we enter this:iex(2)> :erlang.module_info
In Elixir, Erlang modules are represented as atoms. Functions in these modules are invoked in the same way as any Elixir function.
Existing Erlang libraries can be included in Elixir applications, widening the available options. It is also possible to choose an Erlang implementation of a module over Elixir's.
The Elixir standard library has a List
module defined. The Erlang counterpart is lists
.
If we wish to get the last element of a list, we could use both modules:
Sometimes, we need to take a look at what is going on in a running VM. It is useful to see which applications are open and any information about memory usage.
We will use some Erlang modules to inspect a VM instance.
We will follow these steps to get information on our running system:
To get the currently running applications, type this:
iex(1)> :application.which_applications [ {:logger, 'logger', '0.15.1'}, {:iex, 'iex', '0.15.1'}, {:elixir, 'elixir', '0.15.1'}, {:syntax_tools, 'Syntax tools', '1.6.15'}, {:compiler, 'ERTS CXC 138 10', '5.0.1'}, {:crypto, 'CRYPTO', '3.4'}, {:stdlib, 'ERTS CXC 138 10', '2.1'}, {:kernel, 'ERTS CXC 138 10', '3.0.1'} ]
The list that is returned contains three-element tuples. The first element is an atom identifying the application, the second element is the application description, and the third is the application version.
We get information on the memory usage by running the following commands:
iex(2)> :erlang.memory [total: 15474240, processes: 4958016, processes_used: 4957056, system: 10516224, atom: 256313, atom_used: 234423, binary: 15352, code: 6071692, ets: 399560]
It is also possible to get memory usage for atoms, ets tables, binaries, and so on:
iex(3)> :erlang.memory(:atom) 256313
As we saw in the previous recipe, Using Erlang from Elixir, it is possible to seamlessly call Erlang code from Elixir. Even though there is no specific Elixir code to perform these inspections, it is easy to get these abilities via Erlang libraries.
The command line isn't the only way to get information on an Erlang VM. There is a GUI tool named Observer that allows access to information in a more convenient way.
If a GUI-enabled system is available, Observer allows us to open multiple windows with information on the whole system's statistics or even an individual process of that running system.
To use the Observer GUI application, we will follow these steps:
Start the Observer application:
iex(1)> :observer.start :ok
A new window with a tabbed interface will open, and the first information displayed shows CPU information, memory usage, system information, and statistics, as shown in the following screenshot:
Select the Load Charts tab to see graphical representation of memory usage, IO, and scheduler utilization over time, as shown here:
Under the Applications tab, by selecting the kernel application, it is possible to see a representation of an application process's hierarchy, as shown in this screenshot:
Double-click on any of the nodes, for example,
code_server
. A new window will be opened with information for the specific process, as shown in the following screenshot:
In this recipe, we will be using Mix to create a new application.
To create a new Elixir application, follow these steps:
In a command-line session, enter
mix help
to see a list of available tasks:> mix help
Here is what the screen will look like:
To generate a new application, type
mix new simple_app
:> mix new simple_app
What happens next is shown in the following screenshot:
Inside the
simple_app
directory, the generated application is ready to be started. Runiex –S mix
to start the application and verify that everything is working:> iex -S mix Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Interactive Elixir (0.15.1) - press Ctrl+C to exit (type h() ENTER for help) iex(1)>
Nothing happened. So is it working? The absence of messages in the IEx session is a good thing. This generated application behaves more like a library; there's no
main
function like in Java or C. To be sure that the application is responding, edit thelib/simple_app.ex
file by inserting the following code:defmodule SimpleApp do def greet do IO.puts "Hello from Simple App!" end end
Restart the application by pressing Ctrl + C twice and entering
iex –S mix
again.In the IEx session, enter
SimpleApp.greet
.You will see the following output from the application:
iex(1)> SimpleApp.greet Hello from Simple App! :ok iex(2)>
The Elixir application is ready to be used either on your local machine or, if a node is started, it could even be used from another machine.
One of the advantages of OTP (more information on OTP may be found in Chapter 6, OTP – Open Telecom Platform) is modularity, and it is very common to have several applications running as dependencies of one application. An application is a way to achieve modularity; in this context, we call an application something that is known in other programming languages as a library. In this recipe, we will integrate an HTTP client with a new application. We will be using the Hex package manager (http://hex.pm).
Generate a new application with
mix new manage_deps
:> mix new manage_deps
The output is shown in the following screenshot:
We will choose HTTPoison (https://hex.pm/packages/httpoison).
To add a dependency to our application, we will follow these steps:
Inside the
manage_deps
application, openmix.exs
and edit the file to include HTTPoison as a dependency:defp deps do [{:httpoison, "~> 0.4"}] end
HTTPoison must be started with our system. Add this to the started applications list by including it inside the
application
function:def application do [applications: [:logger, :httpoison]] end
Save
mix.exs
and runmix deps.get
to fetch the declared dependencies, as shown in this screenshot:Compile the dependencies by executing
mix deps.compile
, as shown in the following screenshot:Start your application with
iex –S mix
.Inside the IEx session, check whether HTTPoison is running:
iex(1)> :application.which_applications [{:manage_deps, 'manage_deps', '0.0.1'}, {:httpoison, ' Yet Another HTTP client for Elixir powered by hackney\n', '0.4.2'}, {:hackney, 'simple HTTP client', '0.13.1'}(…)]
Get Google's main page using HTTPoison:
iex(5)> HTTPoison.get("http://www.google.com") %HTTPoison.Response{body: "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>302 Moved</TITLE></HEAD><BODY>\n<H1>302 Moved</H1>\nThe document has moved\n<A HREF=\"http://www.google.pt/?gfe_rd=cr&ei=WFAOVLvQFJSs8wfehYJg\">here</A>.\r\n</BODY></HTML>\r\n", headers: %{"Alternate-Protocol" => "80:quic", "Cache-Control" => "private", "Content-Length" => "256", "Content-Type" => "text/html; charset=UTF-8", "Date" => "Tue, 09 Sep 2014 00:56:56 GMT", "Location" => "http://www.google.pt/?gfe_rd=cr&ei=WFAOVLvQFJSs8wfehYJg", "Server" => "GFE/2.0"}, status_code: 302}
Dependencies are preferably added using hex.pm (https://hex.pm/).
Tip
If an application doesn't yet exist in Hex, it is also possible to use a GitHub repository as a source.
To fetch a dependency from GitHub, instead of declaring the dependency with the {:httpoison, "~> 0.4"}
format, the following format is used:
{:httpoison, github: " edgurgel/httpoison "}
The local filesystem may also be configured as a source for dependencies, as follows:
{:httpotion, path: "path/to/httpotion"}
Once the dependencies are declared inside the mix.exs
file, there are Mix tasks to get, compile, and clean them. The dependencies are then fetched, and if these dependencies have more dependencies on themselves, Mix is smart enough to fetch them.
When compiling dependencies, Mix is also capable of figuring out whether any dependent application has its own dependencies and whether they need to be compiled.
Starting IEx with the –S
Mix loads the Mix environment inside IEx, and the application becomes accessible.
As shown in the Inspecting your system recipe, it is possible to get a list of running applications and check whether our dependency (and its own dependencies) are running. In the particular case of HTTPoison, automatic start is ensured by adding the atom representing the application name to the list under applications
([applications: [:logger, :httpoison]]
).
The documentation on Hex usage available at https://hex.pm/docs/usage.
The Elixir documentation on Mix tasks is available at http://elixir-lang.org/docs/stable/mix/.
An application may be generated with a supervision tree to monitor processes. The supervision tree must be started and stopped with the application, and to do so, an application module callback must also be implemented. Mix provides a simple way to generate this type of application.
To generate an application with a supervision tree and an application module callback, we run mix new supervised_app –-sup
in the command line. This is shown in the following screenshot:

When mix new task
is invoked with the –-sup
option, although the generated application appears to be identical to the application created in the Creating a simple application recipe, a few things change, which are as follows:
supervised_app/mix.exs
def application do
[applications: [:logger],
mod: {SupervisedApp, []}]
end
An application module callback is added like this:
supervised_app/lib/supervised_app.ex defmodule SupervisedApp do use Application def start(_type, _args) do import Supervisor.Spec, warn: false children = [ # Define workers and child supervisors to be supervised # worker(SupervisedApp.Worker, [arg1, arg2, arg3]) ] opts = [strategy: :one_for_one, name: SupervisedApp.Supervisor] Supervisor.start_link(children, opts) end end
The Application
module behavior is declared, and a start
function must be defined to comply with this behavior. Inside the start
function, a list of children (usually worker processes) is declared, and so are the supervision options (opts
). The supervisor is then started, passing the list of processes to be supervised and the options.
The documentation for the
Application
module can be accessed at http://elixir-lang.org/docs/stable/elixir/Application.html.Information on the
Supervisor
module is available at http://elixir-lang.org/docs/stable/elixir/Supervisor.html.
The "Erlang way" is to name each self-contained unit of code as an app. Sometimes, an app may be what is referred to as a library in other languages. This is a great way to achieve code reusability and modularity, but sometimes, it is convenient to treat all the apps in a project as a single entity, committing them as a whole to version control, to allow running tests, and so on. Think of an umbrella application as a container used to hold one or more applications and to make them behave as a single application.
This recipe shows how to create umbrella applications with Mix.
Generate an umbrella application to contain other applications:
mix new --umbrella container
What happens next is shown in the following screenshot:
Generate
application_one
andapplication_two
inside thecontainer/apps
directory:> cd container/apps > mix new application_one > mix new application_two
Modify the tests in the applications as follows:
Change the test in
container/apps/application_one/application_one_test.exs
like this:test "the truth on application one" do IO.puts "Running Application One tests" assert 1 + 1 == 2 end
Change the test in
container/apps/application_two/application_two_test.exs
as shown here:test "the truth on application two" do IO.puts "Running Application Two tests" assert 2 - 1 == 1 end
Run the tests in all applications (inside the container directory):
> mix test
The result of these tests is shown here:
Now run the tests individually. Firstly, run them for
application_one
as follows:> cd apps/application_one > mix test
The outcome of these tests is shown in the following screenshot:
For
application_two
, run them like this:> cd ../application_two > mix test
The result of these tests is shown in this screenshot:
By generating this structure of the application with subprojects under the apps
directory, Elixir makes dependency management, compilation, and testing easier. It is possible to perform these tasks at the umbrella application level, affecting all the subprojects, or at each subproject level, allowing a high level of granularity.
The Elixir Getting Started guide on dependencies and umbrella projects is available at http://elixir-lang.org/getting_started/mix_otp/7.html. It says the following:
Remember that the runtime and the Elixir ecosystem already provide the concept of applications. As such, we expect you to frequently break your code into applications that can be organized logically, even within a single project. However, if you push every application as a separate project to a Git repository, your projects can become very hard to maintain, because now you will have to spend a lot of time managing those Git repositories rather than writing your code.
For this reason, Mix supports "umbrella projects." Umbrella projects allow you to create one project that hosts many applications and push all of them to a single Git repository. That is exactly the style we are going to explore in the next sections.
Mix tasks run in a specific environment. The predefined environments are production, development, and test (prod, dev, and test). The default environment is dev. In this recipe, we will configure an application with different values for each environment. Invoking the same function will result in a different output based on the configuration.
To manage an application configuration, we follow these steps:
Create a new application:
> mix new config_example
Go to the generated application directory and open
config/config.exs
.Replace all of the file's content with the following code:
use Mix.Config config :config_example, message_one: "This is a shared message!" import_config "#{Mix.env}.exs"
Create three more files under the
config
directory with the following code:In
config/dev.exs
, add the following:use Mix.Config config :config_example, message_two: "I'm a development environment message!"
In
config/prod.exs
, add this code:use Mix.Config config :config_example, message_two: "I'm a production environment message!"
In
config/test.exs
, add the following:use Mix.Config config :config_example, message_two: "I'm a test environment message!"
Define two module attributes in
lib/config_example.ex
to hold the values ofmessage_one
andmessage_two
, as follows:@message_one Application.get_env(:config_example, :message_one) @message_two Application.get_env(:config_example, :message_two)
Create a
show_messages
function inlib/config_example.ex
, like this:def show_messages do IO.puts "Message one is: #{@message_one}" IO.puts "Message two is: #{@message_two}" end
Start the application in the three different environments and see the output of the
show_messages
function:For the development environment, start the application as follows:
> MIX_ENV=dev iex –S mix iex(1)> ConfigExample.show_messages Message one is: This is a shared message! Message two is: I'm a development environment message! :ok iex(2)>
For the production environment, start the application like this:
> MIX_ENV=prod iex –S mix iex(1)> ConfigExample.show_messages Message one is: This is a shared message! Message two is: I'm a production environment message! :ok iex(2)>
For the test environment, start the application as follows:
> MIX_ENV=test iex –S mix iex(1)> ConfigExample.show_messages Message one is: This is a shared message! Message two is: I'm a test environment message! :ok iex(2)>
When we include the last line in config.exs
(import_config "#{Mix.env}.exs"
), the Mix configuration is loaded from the files, in this case with the Mix environment as its name and .exs
as its extension.
The configuration from the imported files will override any existing configuration (with the same key) in the config.exs
file. In fact, Configuration values are merged recursively. See the example at https://github.com/alco/mix-config-example.
To access configuration values, we use Application.get_env(:app, :key)
.
Sometimes, the existing Mix tasks just aren't enough. Fortunately, Mix allows the creation of customized tasks that integrate as if they were shipped with Mix itself. In this recipe, we will create a custom Mix task that will print the Erlang VM memory status.
The steps required to create a custom task are as follows:
Create a new file,
meminfo.ex
, that defines theMeminfo
module insideMix.Tasks
:defmodule Mix.Tasks.Meminfo do use Mix.Task end
Add the new task description to be displayed when
mix help
is invoked:@shortdoc "Get Erlang VM memory usage information"
Add the new task module documentation:
@moduledoc """ A mix custom task that outputs some information regarding the Erlang VM memory usage """
Create a
run/1
function:def run(_) do meminfo = :erlang.memory IO.puts """ Total #{meminfo[:total]} Processes #{meminfo[:processes]} Processes (used) #{meminfo[:processes_used]} System #{meminfo[:system]} Atom #{meminfo[:atom]} Atom (used) #{meminfo[:atom_used]} Binary #{meminfo[:binary]} Code #{meminfo[:code]} ETS #{meminfo[:ets]} """ end
Compile the code using the Elixir compiler,
elixirc
:elixirc meminfo.ex
No message should appear but a file named
Elixir.Mix.Tasks.Meminfo.beam
is created.Run
mix help
to see the new task listed and its short description:> mix help mix # Run the default task (current: mix run) mix archive # List all archives (…) mix meminfo # Get Erlang VM memory usage information mix new # Create a new Elixir project mix run # Run the given file or expression mix test # Run a project's tests iex -S mix # Start IEx and run the default task
Execute the custom task:
> mix meminfo Total 17692216 Processes 4778984 Processes (used) 4777656 System 12913232 Atom 339441 Atom (used) 321302 Binary 14152 Code 8136817 ETS 452832
Mix tasks are just modules that are declared as Mix.Tasks.<MODULENAME>
with a run
function defined.
In meminfo.ex
, we use the Mix.Task
module by declaring use Mix.Task
. The use
directive allows us to use a given module in the current context.
The @shortdoc
attribute allows us to define a short description to display when some help on Mix or the mix.task
is displayed.
The run/1
function is the place where all of the task's work is done. In this particular case, we use an Erlang function to return a keyword list with several entries, and print them for the user in a formatted way.