Functional programming with Elixir

Magic Potion

© Lead Image © Csaba Arva, 123RF.com

© Lead Image © Csaba Arva, 123RF.com

Article from Issue 181/2015
Author(s):

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.

Figure 1: The sample application uses the localhost loopback network to simulate three server nodes.

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

wget http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i erlang-solutions_1.0_all.deb
sudo apt-get update
sudo apt-get install elixir erlang-dev

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).

Figure 2: The interactive session with iex is useful for experiments.

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

httpd
|- config
   |- config.exs
|- lib
   |- httpd.ex
|- mix.exs
|- README.md
|- test
   |- httpd_test.exs
   |- test_helper.exs

Listing 3

httpd/mix.exs (Project Configuration)

01 defmodule Httpd.Mixfile do
02   use Mix.Project
03
04   def project do
05     [app: :httpd,
06      version: "0.0.1",
07      elixir: "~> 1.0",
08      deps: deps]
09   end
10
11   defp deps do
12     [{:cowboy, "~> 1.0.0"},
13       {:plug, "~> 0.13"}]
14   end
15 end

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)

01 defmodule Httpd do
02   use Plug.Router
03   use Plug.Builder
04
05   plug Plug.Static, at: "/", from: "/home/pa"
06   plug :match
07   plug :dispatch
08
09   get "/info" do
10     send_resp(conn, 200, inspect(conn))
11   end
12
13   match _ do
14     send_resp(conn, 404, "Not found!")
15   end
16 end

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).

Figure 3: send_resp() terminates the pipeline and sends an HTTP response to the web server.

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.

Figure 4: The Plug.Conn request object has a struct data type. The value stores the typical parameters of a HTTP request.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Crystal

    Crystal is an open source project that seeks to combine the best of two worlds: the simplicity of a language syntax similar to Ruby and the speed and capabilities of the LLVM platform.

  • Nginx

    The fast and practical Nginx web server is easy to configure and extend.

  • Docker with OwnCloud

    Run your application smoothly and portably in the cloud with the Docker container system. This workshop takes a practical look deploying Docker with the OwnCloud cloud environment.

  • Perl: Asynchronous Code

    Asynchronous program flow quickly degenerates into unreadable code if it lacks an overarching concept to provide structure. Fortunately, the JavaScript community has invented some functional tricks that also help tame asynchronous Perl code.

  • Apache HTTP Server 2.4

    Apache HTTP Server version 2.4 is full of exciting new features. We share a few of them with you.

comments powered by Disqus

Direct Download

Read full article as PDF:

Price $2.95

News

njobs Europe
What:
Where:
Country:
Njobs Netherlands Njobs Deutschland Njobs United Kingdom Njobs Italia Njobs France Njobs Espana Njobs Poland
Njobs Austria Njobs Denmark Njobs Belgium Njobs Czech Republic Njobs Mexico Njobs India Njobs Colombia