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
-
AlmaLinux 10.0 Beta Released
The AlmaLinux OS Foundation has announced the availability of AlmaLinux 10.0 Beta ("Purple Lion") for all supported devices with significant changes.
-
Gnome 47.2 Now Available
Gnome 47.2 is now available for general use but don't expect much in the way of newness, as this is all about improvements and bug fixes.
-
Latest Cinnamon Desktop Releases with a Bold New Look
Just in time for the holidays, the developer of the Cinnamon desktop has shipped a new release to help spice up your eggnog with new features and a new look.
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.