Functional programming with Elixir
Magic Potion

© Lead Image © Csaba Arva, 123RF.com
Developers will appreciate Elixir's ability to build distributed, fault-tolerant, and scalable applications.
Elixir 1.0 [1] offers easy entry into the world of functional programming by relying on the Erlang [2] virtual machine. An Elixir program can invoke any Erlang function with no run-time cost. In this article, I demonstrate Elixir's capabilities by rolling out the distributed server system shown in Figure 1. The system runs a proxy on the first server node and forwards incoming HTTP requests to one of two server nodes on the local network to boost performance. Both nodes store the content redundantly and deliver identical documents on request that are delivered as responses to the clients via the proxy.

Friendly Parasite
Listing 1 shows the installation of the current version of Elixir (version 1.0.4-1) on Ubuntu 14.04. Line 1 uses wget
to pick up a Debian package that points to an external package repository with the current versions of Elixir and Erlang. The dpkg
package manager bundles the list in line 2 to the correct location on the filesystem; the next line updates and parses the list. Finally, Elixir and the erlang-dev
packages are installed on the computer.
Listing 1
Installing Elixir
The iex
command launches an interactive session in the shell after completing the install. The expression
:crypto.md5("Using crypto from Erlang OTP")
tests whether Elixir is compatible with Erlang by calling the md5()
function from Erlang's Crypto module. In the shell, iex
should display the string returned by md5()
as a byte sequence enclosed in angled brackets (Figure 2).
In addition to byte sequences, Elixir supports tuples, lists, and structs for storing structured data. The list
[{:app, :httpd}, {:version, "0.0.1"}]
stores the {:app, :httpd}
tuple in its header; the tuple in turn contains two literals: :app
and :httpd
. Literals that are introduced by a :
are known in Elixir-speak as atoms. The language makes liberal use of them.
To evaluate data structures, Elixir comes with patterns, emulating the style of Haskell [3]. An example of a pattern can be seen to the left of the equals sign in the following expression
[{a, b}|_] = [my: :house, your: :house]
Before Elixir compares it with the list on the right, it first converts to the standard form [{:my, :house}, {:your :house}]
.
On comparing the pattern, the tuple in the list header on the left side matches the tuple in the list header on the right side. Both tuples store two elements. The variables a
and :my
and b
and :house
are tangible after the compare. The _
operator to the right of the pipe symbol in the pattern matches any expression; in this example, :your :house
. Figure 2 shows the values from a
and b
again after pattern matching.
Everything Under Control
The mix
build tool helps implement Elixir projects. When started at the command line, it compiles, launches, and tests projects. Just like iex
, mix
ends up on your computer when you install Elixir. The
mix new httpd
command creates the application skeleton for the planned web server (Listing 2). The mix.exs
file stores the project configuration (Listing 3); line 1 uses the defmodule
keyword to define an Elixir module named Httpd.Mixfile
; the code then continues with a do
/end
block to line 15.
Listing 2
Application Skeleton
Listing 3
httpd/mix.exs (Project Configuration)
Line 2 calls use
to integrate the Mix.Project
macro. Public functions such as project()
are introduced by the def
keyword, whereas defp
is used for private functions and deps()
. The keywords are followed by the function name and an optional list of parameters. The do
/end
block that follows defines the return values of the functions.
The call to the function project()
(lines 4-9) returns the project's core data in the form of a list, and deps()
(line 11) lists the dependencies of the project. They include the Cowboy [4] web server framework (version 1.0.0 or newer), which is written in Erlang, and Plug [5] middleware (version 0.13 or newer).
Private functions can only be called within the module. If you type Http.Mixfile.deps()
in the interactive iex
shell, you will see an (UndefinedFunctionError
) message.
Advanced Code
Listing 4 shows the code for lib/httpd.ex
, in which the Httpd module responds to HTTP requests, invoking the Elixir Plug module to do so. The module processes HTTP requests in the pipeline in a fashion similar to the Node.js Connect middleware framework [6].
Listing 4
httpd/lib/httpd.ex (Httpd Module)
The Plug framework first fields a request object from a web server and passes it on to a number of filter functions, or plugs. The plugs evaluate the object, modify it, and delegate it to a downstream plug. Alternatively, the plugs tell the framework to terminate the pipeline by calling the send_resp()
function (lines 10 and 14). They use the function's call parameters to construct an HTTP response, which they then return to the web server (Figure 3).
Listing 4 first binds the Plug.Router
and Plug.Builder
macros. The plug()
function (lines 5-7) registers the plugs and keeps them in a FIFO (first in, first out) queue. As mentioned earlier, the program processes the queue as soon as a request object arrives.
The Static
plug in line 5 creates a URL that matches a location on the server's directory system. Because of the at:
statement, the URL http://127.0.0.3/info.html sends the /home/pa/info.html
file to the client. If no file matches the URL, the downstream plug picks up the HTTP request. The :match
and :dispatch
statements work hand in hand. Whereas :match
uses pattern matching to find suitable HTTP requests and forwards matches to :dispatch
, :dispatch
takes care of the HTTP response.
The code blocks in lines 9-11 and 13-15, do not contain any native Elixir code. Instead, they are code extensions created by the plug's inventor [7]. The plug router macro converts these code blocks into valid tree components when building the syntactical tree. Theoretically, you could implement the code block as an Elixir expression:
{method: "get", url: "/info", _} -> \ Usend_resp(conn, 200, inspect(conn))
Listing 4 takes a different approach, however. Lines 9-11 output a formatted version of the request object in the body of the HTTP response to the /info
URL thanks to the inspect()
function.
The function writes 200 as a response code to the response header. For any requests that have not received a response, lines 13-15 return the well-known 404 message to the client via the web server. Before starting the application, the user needs to change to the project directory then build the application by running Mix. The command first loads the source code of the required modules from Hex.pm [8], the package archive shared by Erlang and Elixir; then, the application is compiled into a bevy of BEAM files. The files end up in the project directory below the _build
folder. The iex
command again starts an interactive session:
cd httpd mix deps.get mix deps.compile sudo iex -S mix
The function call
Plug.Adapters.Cowboy.http Httpd, [], \ ip: {127, 0, 0, 3}, port: 80
starts an instance of the web server from the Cowboy framework. The server is then reachable via the IP address 127.0.0.3 and port 80; it loads the module from Listing 4, which then waits for HTTP requests. Figure 4 shows a formatted request object as a response to the request URL http://127.0.0.3/info in the Firefox browser view.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.
News
-
The GNU Project Celebrates Its 40th Birthday
September 27 marks the 40th anniversary of the GNU Project, and it was celebrated with a hacker meeting in Biel/Bienne, Switzerland.
-
Linux Kernel Reducing Long-Term Support
LTS support for the Linux kernel is about to undergo some serious changes that will have a considerable impact on the future.
-
Fedora 39 Beta Now Available for Testing
For fans and users of Fedora Linux, the first beta of release 39 is now available, which is a minor upgrade but does include GNOME 45.
-
Fedora Linux 40 to Drop X11 for KDE Plasma
When Fedora 40 arrives in 2024, there will be a few big changes coming, especially for the KDE Plasma option.
-
Real-Time Ubuntu Available in AWS Marketplace
Anyone looking for a Linux distribution for real-time processing could do a whole lot worse than Real-Time Ubuntu.
-
KSMBD Finally Reaches a Stable State
For those who've been looking forward to the first release of KSMBD, after two years it's no longer considered experimental.
-
Nitrux 3.0.0 Has Been Released
The latest version of Nitrux brings plenty of innovation and fresh apps to the table.
-
Linux From Scratch 12.0 Now Available
If you're looking to roll your own Linux distribution, the latest version of Linux From Scratch is now available with plenty of updates.
-
Linux Kernel 6.5 Has Been Released
The newest Linux kernel, version 6.5, now includes initial support for two very exciting features.
-
UbuntuDDE 23.04 Now Available
A new version of the UbuntuDDE remix has finally arrived with all the updates from the Deepin desktop and everything that comes with the Ubuntu 23.04 base.