Functional programming with Elixir
Magic Potion
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
-
Fedora 41 Beta Available with Some Interesting Additions
If you're a Fedora fan, you'll be excited to hear the beta version of the latest release is now available for testing and includes plenty of updates.
-
AlmaLinux Unveils New Hardware Certification Process
The AlmaLinux Hardware Certification Program run by the Certification Special Interest Group (SIG) aims to ensure seamless compatibility between AlmaLinux and a wide range of hardware configurations.
-
Wind River Introduces eLxr Pro Linux Solution
eLxr Pro offers an end-to-end Linux solution backed by expert commercial support.
-
Juno Tab 3 Launches with Ubuntu 24.04
Anyone looking for a full-blown Linux tablet need look no further. Juno has released the Tab 3.
-
New KDE Slimbook Plasma Available for Preorder
Powered by an AMD Ryzen CPU, the latest KDE Slimbook laptop is powerful enough for local AI tasks.
-
Rhino Linux Announces Latest "Quick Update"
If you prefer your Linux distribution to be of the rolling type, Rhino Linux delivers a beautiful and reliable experience.
-
Plasma Desktop Will Soon Ask for Donations
The next iteration of Plasma has reached the soft feature freeze for the 6.2 version and includes a feature that could be divisive.
-
Linux Market Share Hits New High
For the first time, the Linux market share has reached a new high for desktops, and the trend looks like it will continue.
-
LibreOffice 24.8 Delivers New Features
LibreOffice is often considered the de facto standard office suite for the Linux operating system.
-
Deepin 23 Offers Wayland Support and New AI Tool
Deepin has been considered one of the most beautiful desktop operating systems for a long time and the arrival of version 23 has bolstered that reputation.