Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering Elixir

You're reading from   Mastering Elixir Build and scale concurrent, distributed, and fault-tolerant applications

Arrow left icon
Product type Paperback
Published in Jul 2018
Publisher Packt
ISBN-13 9781788472678
Length 574 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
André Albuquerque André Albuquerque
Author Profile Icon André Albuquerque
André Albuquerque
Daniel Caixinha Daniel Caixinha
Author Profile Icon Daniel Caixinha
Daniel Caixinha
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Preparing for the Journey Ahead 2. Innards of an Elixir Project FREE CHAPTER 3. Processes – The Bedrock of Concurrency and Fault Tolerance 4. Powered by Erlang/OTP 5. Demand-Driven Processing 6. Metaprogramming – Code That Writes Itself 7. Persisting Data Using Ecto 8. Phoenix – A Flying Web Framework 9. Finding Zen through Testing 10. Deploying to the Cloud 11. Keeping an Eye on Your Processes 12. Other Books You May Enjoy

Tooling and ecosystems

The last section of this chapter is dedicated to one of the most renowned characteristics of Elixir: its incredible tooling. Also, since we can directly use Erlang libraries, we can take advantage of a very mature ecosystem that has been around for decades. First, let's begin with Elixir's interactive shell, IEx.

IEx

In the beginning of this chapter, we provided some instructions on how to start and stop an Elixir shell. You've seen it in use throughout this chapter. We'll now show you some other interesting things you can do inside IEx.

In this chapter, we've been giving you links to the official documentation of several Elixir modules. You can access this documentation right from IEx, using the h command. Let's say you want to know more about the Enum module:

iex> h Enum
Enum
#...,

The preceding code provides a set of algorithms that enumerate over enumerables according to the Enumerable protocol.

Note that, for brevity, the output of this command was cropped.

You can even pass a fully qualified function name, and get information about it. Let's say, for example, you don't remember the order of the arguments for the Enum.map/2 function:

iex> h Enum.map/2
def map(enumerable, fun)
@spec map(t(), (element() -> any())) :: list()

Returns a list where each item is the result of invoking fun on each
corresponding item of enumerable.
For maps, the function expects a key-value tuple.
## Examples

iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
[2, 4, 6]

iex> Enum.map([a: 1, b: 2], fn({k, v}) -> {k, -v} end)
[a: -1, b: -2]

Another very interesting feature, which ships from Elixir 1.5 onwards, is the ability to set breakpoints from IEx. To showcase this, let's create a breakpoint on the StringHelper.palindrome?/1 function we've defined in the Functions and modules section, present in the "string_helper.ex" file:

iex> break! StringHelper.palindrome?/1
1
iex> StringHelper.palindrome?("abba")
Break reached: StringHelper.palindrome?/1 (string_helper.ex:2)

1: defmodule StringHelper do
2: def palindrome?(term) when is_bitstring(term) do
3: String.reverse(term) == term
4: end

pry(1)> term
"abba"
pry(2)> whereami
Location: string_helper.ex:2

1: defmodule StringHelper do
2: def palindrome?(term) when is_bitstring(term) do
3: String.reverse(term) == term
4: end

As you can see, you could access the argument passed to the function, term, and also use whereami to show the location where the breakpoint stopped the execution. To resume the execution, you use the continue command to go to the next breakpoint, or respawn to exit and start a new shell.

From Elixir 1.6 onward, you can also use pattern matching and guard clauses when setting a breakpoint. The breakpoint we defined earlier could also be set as break! StringHelper.palindrome?("abba"), which would make our breakpoint work when that function is called with "abba" as the argument.

You can also use IEx.pry to set a breakpoint on the source-code file, instead of doing it via IEx. Use the method that's most appealing to you.

To finish this section, we'll show you a handy feature of IEx: the ability to access values from the history. Sometimes, you call a certain function and realize afterward that you wanted to bind the result of that function call to a variable. If this happens, you can use the v/1 function. You pass it a number, which represents the position of the expression from which you want to retrieve the value (starting at 1). You can also pass a negative number, which makes the position relative to the current expression in the shell. You can call this function without providing an argument, which is the same as calling v(-1)which means we're getting the value from the last expression. Let's see an example:

iex> 7 * 3
21
iex> a = v
21
iex> a * 2
42
iex> v(-3)
21

There are more things you can do with IEx, such as configuring it or connecting to remote shells. Please refer to the official documentation (at https://hexdocs.pm/iex/IEx.html) for further usage examples of IEx.

Mix

Mix is the Swiss-army knife of the Elixir tools. It's used to compile and run your application, manage your dependencies, run tests, and even for profiling your code. You can see which Mix tasks are available by typing mix help in your terminal. We won't explain its usage here, since the next chapter's purpose is to show you how you can use Mix to create and maintain an Elixir project.

ExUnit

Elixir comes with a fully fledged unit test framework: ExUnit. We'll use it to write tests for the application built in the course of this book. Chapter 9, Finding Zen Through Testing, is dedicated to testing, where we'll demonstrate how you can write tests for your application that let you sleep at night.

Erlang interoperability

As we stated at the beginning of this chapter, Elixir targets the Erlang runtime. Elixir is compiled to byte-code that can run on an Erlang VM (or BEAM), and Erlang libraries can be used in your Elixir projects (and vice versa). The philosophy in the Elixir community is to not reinvent the wheel and directly use Erlang libraries when appropriate. The creation of Elixir libraries that simply wrap an underlying Erlang library is discouraged, as you can directly call an Erlang library from your Elixir code.

We can take advantage not only of Erlang libraries, but also of their tooling. For instance, Erlang ships with a tool called Observer, which allows you to monitor your server and provides you with tons of useful information about the Erlang VM, such as the running processes or dynamic charts of load. We'll explore this tool in greater detail in Chapter 11Keeping an Eye on Your Processes, when we talk about monitoring.

To utilize an Erlang library, you write its name as an atom and then call functions on it. Elixir doesn't have a Math module, so when more advanced mathematical operators are needed, it's common to call the math Erlang library. Let's use this library to calculate the natural logarithm of 10:

iex> :math.log(10)
2.302585092994046
You can check out Erlang's standard libraries at http://erlang.org/doc/apps/stdlib/index.html.

When we introduced the data types in Elixir, we mentioned the Port type, which is a reference to a resource used by the Erlang VM to interact with external resources. Let's now see how we can use them to interact with an operating system process. To interact with ports, we use functions from the Port module. In the following example, we'll use the whoami UNIX command, which prints the username associated with the current user. To do that, we open a port and provide the name of the executable we want to run:

iex> port = Port.open({:spawn, "whoami"}, [:binary])
#Port<0.3730>

We passed the [:binary] option so that we get our result as a binary instead of a list of bytes. We now use the IEx flush() helper to print the messages received by the port:

iex> flush()
{#Port<0.3731>, {:data, "dcaixinha\n"}}
:ok

As our operating system process died after returning its result, the port is also closed. If this was not the case, we could use the Port.close/1 function to explicitly close the port. Also note that this was a very simple example. You can have more complex interactions by using the Kernel.send/2 function to dynamically send messages (that contain commands) to your port. In the official documentation for ports (which is available at https://hexdocs.pm/elixir/Port.html), you can see how this can be achieved.

If all we're interested in is running an operating-system command and getting its result back, we can use the System.cmd/3 function, which is an abstraction on top of ports that allows us to achieve this effortlessly.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime