There have been good posts written about creating Elixir libraries already. One awesome recent post can be found here. It will walk you throught the process of writing, documenting and publishing your first Elixir library really well.

One thing that was missing for me, however, was a short description on how and why you can wrap your code into reusable OTP application. Let’s fix that.

Two types of Elixir libraries

You probably already noticed that there are two types of libraries in Elixir. Some libraries expose just modules and their functions, so the code can be reused between different projects. When you include such library, you only need to do the following in mix.exs :

def deps do [{:uuid, "~> 1.1"}] end

when you run mix deps.get , and compile the project, you can start using UUID module straight away, right from your code.

Second class of Elixir libraries allow, or even requires, that you start another OTP application. Popular example is httpoison, HTTP client library, that asks you to do the following in your mix.exs :

def deps do [{:httpoison, "~> 0.8.0"}] end def application do [applications: [:httpoison]] end

If your project uses Phoenix web framework, it already starts a few more applications by default:

def application do [:phoenix, :phoenix_html, :cowboy, :logger, :phoenix_ecto, :postgrex] end

OTP applications

Creating an Elixir library as an OTP application, allows you to do slightly more than code sharing. An application usually starts it’s own supervision tree. It can be stopped manually whenever needed, and started with different set of parameters without affecting the rest of the system.

Erlang/Elixir can start multiple OTP applications in one instance of VM. This means OTP applications are loosely coupled, are visible to each other, and processes between different applications can easily communicate (provided they know each other’s PIDs or registered names). When your Elixir project starts, list of applications is provided by application/0 function as shown above. Those applications are started in order provided.

You can inspect the list of known and started applications easily using command line, or GUI tools that ship with Erlang.

Command line:

iex(1)> :application.info [loaded: [{:chatbot, 'chatbot', '0.0.1'}, {:logger, 'logger', '1.2.5'}, {:compiler, 'ERTS CXC 138 10', '6.0.3'}, {:mix, 'mix', '1.2.5'}, {:stdlib, 'ERTS CXC 138 10', '2.8'}, {:iex, 'iex', '1.2.5'}, {:kernel, 'ERTS CXC 138 10', '4.2'}, {:elixir, 'elixir', '1.2.5'}], loading: [], started: [chatbot: :temporary, logger: :temporary, mix: :temporary, iex: :temporary, elixir: :temporary, compiler: :temporary, stdlib: :permanent, kernel: :permanent], start_p_false: [], running: [chatbot: #PID<0.111.0>, logger: #PID<0.102.0>, mix: #PID<0.66.0>, iex: #PID<0.48.0>, elixir: #PID<0.41.0>, compiler: :undefined, stdlib: :undefined, kernel: #PID<0.9.0>], starting: []]

GUI:

:iex(1)> :observer.start()

Applications are also useful if you need to perform extra configuration, and initialize your library accordingly. For example, logger can be configured in your config/config.exs this way:

config :logger, :console, format: "$time $metadata[$level] $message

", metadata: [:request_id]

The configuration can be read by the library using Application.get_env/3.

Each Elixir project will start several applications, including :logger , :iex , and even :elixir application - those things must be really handy then!

Let’s get our hands dirty

We will build a (very trendy recently) chat bot. Our chat bot will be Elixir library, that we can share between our projects, or even publish to hex.pm.

First, let’s generate our project, that will be a client for our chatbot:

$> mix new project mix new project * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/project.ex * creating test * creating test/test_helper.exs * creating test/project_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd project mix test Run "mix help" for more commands.

Next, let’s generate our chatbot application library:

$> mix new chatbot --sup * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/chatbot.ex * creating test * creating test/test_helper.exs * creating test/chatbot_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd chatbot mix test Run "mix help" for more commands.

Please note the --sup in the second invocation. We are telling mix this way to generate supervision tree for our chatbot application. If you compare chatbot/lib/chatbot.ex with project/lib/project.ex , you will notice the former has some extra generated code:

defmodule Chatbot do use Application # See http://elixir-lang.org/docs/stable/elixir/Application.html # for more information on OTP Applications def start(_type, _args) do import Supervisor.Spec, warn: false children = [ # Define workers and child supervisors to be supervised # worker(Chatbot.Worker, [arg1, arg2, arg3]), ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: Chatbot.Supervisor] Supervisor.start_link(children, opts) end end

this is scaffold for our application callback module, that by default would start an empty supervision tree.

In both projects, mix.exs files also differ. Our chatbot points mix to our application callback module, so it knows what should be started:

def application do [applications: [:logger], mod: {Chatbot, []}] end

Let’s write some AI code ;) for our chat bot:

chatbot/lib/chatbot/ai.ex:

defmodule Chatbot.Ai do use GenServer def start_link(name) do GenServer.start_link(__MODULE__, nil, [name: name]) end def handle_call(message, _from, nil) do {:reply, "Hello, stranger! What's your name?", "stranger"} end def handle_call(message, _from, "stranger") do {:reply, "Nice to meet you, #{message}. What you've been up to recently?", message} end def handle_call(message, _from, state) do {:reply, "This is interesting, #{state}! Tell me more on this...", state} end end

chatbot/lib/chatbot.ex:

defmodule Chatbot do def start(_type, _args) do ... children = [ worker(Chatbot.Ai, [Chatbot.Ai]), ] ... end def say(msg) do GenServer.call Chatbot.Ai, msg end end

The code above implements simple chat bot as GenServer and starts it when chatbot application starts, under a default supervisor.

Let’s include chat bot library in our project:

project/mix.exs:

def application do [applications: [:logger, :chatbot]] end defp deps do [{:chatbot, path: "../chatbot"}] end

If we spawn our project’s interactive elixir shell, we can have a nice chat with our bot:

$> iex -S mix iex(1)> Chatbot.say "Hi there" "Hello, stranger! What's your name?" iex(2)> Chatbot.say "Hubert" "Nice to meet you, Hubert. What you've been up to recently?" iex(3)> Chatbot.say "Advanced AI using Elixir" "This is interesting, Hubert! Tell me more on this..."

By including our chat bot as an application in a separate library, we did not have to take care of intialization in our main project. We can decide to stop and later start our application at any stage, as a unit, from a running project:

iex(4)> :application.stop(:chatbot) :ok [info] Application chatbot exited: :stopped iex(5)> :application.start(:chatbot) :ok

Summary

OTP applications are a handy way of encapsulating code into logical chunks, such as libraries. If your library needs to perform initialization based on configuration, store state, set up custom supervision tree - this is a way forward.