We chose Elixir to build our chatbot

And we’re glad we did!

Building a chatbot is far from a trivial task: better make sure you use the right tools for the job. In this article, we explain why we think Elixir is a good candidate.

At Nu Echo, for a couple of months now, we’ve been working on a conversational, task-oriented chatbot. The project uses multiple technologies, but what holds everything together is a certain amount of Elixir code. My colleague Dominique Boucher already talked a little about that language in a recent article on the importance of programming languages in innovation.

Today, I want to approach the subject from another angle, and expose some of the reasons we believe Elixir was (and still is!) a wise choice to build our chatbot. Before we started working on the chatbot, we chose Elixir not only because we wanted to experiment with different programming paradigms, but also because it seemed a good language to rapidly build a prototype, especially if we were to build it around reactive web sockets. We are still very much happy with Elixir, but our reasons for liking the language are not exactly the same.

Elegant, easy to learn syntax

Elixir is often described as basically Erlang, but with a Ruby-style syntax. The comparison is fair as both syntaxes bear striking similarities (in a good way), but in fact, Elixir is so much more than just a thin layer of syntactic sugar over the Erlang Virtual Machine (also known as BEAM). It is truly a joy to code in Elixir.

The language has been designed with great care and consistency, and it shows.

One great feature of the Elixir syntax is the pipeline (represented by the |> operator). Basically, it’s simple syntactic sugar to represent function chaining in a way that is easy to read and modify. The only thing that is not self-explanatory about the pipeline is that the first parameter of each piped function is implicitly the result of the previous function or variable in the pipeline. Here is an example of pipeline usage in our code, in the function that receives the user input and process it to generate the bot’s response:

def process(%Plug.Conn{params: params} = conn, bot_request) do
locale = Map.get(params, “locale”, nil)
|> get_conversation(@dialogue_path, locale)
|> parse_input
|> post_processing
|> execute_dialogue
|> render_output
|> request_assistance
|> send_output
|> update_conversation
|> return_response(conn)

The flow of information is clear, and it is really easy to change the order of execution (if we need to do that) by switching lines around in the pipeline. This operator also improves the code readability (which is important since this function is actually the backbone of our chatbot): an alternative to the pipeline would be to save each intermediate result in its own variable, or to chain the functions by nesting them. Either way would work, but would be really ugly and harder to maintain.

Since developing a chatbot involves writing quite a lot of code, it helps to be able to work with a language that promotes concision and readability. Of course, Elixir is not the only language doing exactly that, but it also offers other advantages.

The advantages of immutability

While it’s always a plus to like a programming language, it is rarely the determining factor for choosing it. It must offer some functionality that place it above other options. For Elixir, one of them is immutability.

In essence, an immutable object cannot be modified after its creation. This simple fact can help to bring a lot of stability in the development process. It may seem counter-intuitive, as applications are, at the core, all about handling data and modifying it. The thing is, instead of directly modifying data, you compute new data from old one, transforming it in a functional way. From the outside, the result really look like state modification, but it’s not.

For example, if you want to modify a variable, you simply create another one and use it instead. The new version exists alongside the previous one. This guarantees that a reference to a piece of data will always be valid and safe from corruption.

Immutability, when fully embraced, boosts the confidence in the code base and drives the design towards safer and more efficient patterns. We found that this feature was particularly useful to clearly represent each step of the evolution of the chatbot conversation context in the dialog process. Also, to be able to think about each possible state in isolation to the others is a great way to simplify the problem at hand.

The rock-solid foundations of BEAM

As I mentioned earlier, Elixir is built over the battle-tested Erlang Virtual Machine, and thus inherits a lot of the cool features of Erlang, like the ability to scale without devouring absurd amounts of system resources, or the possibility to build a fault-tolerant application by distributing it amongst multiple nodes. It is even possible to call native Erlang functions with no runtime cost.

Granted, none of these features actually help us to develop a prototype, but most of them, especially regarding scalability and fault-tolerance, will look really interesting when comes the time to actually roll out a chatbot in production. Especially as Erlang was designed for highly reliable telecom switches in the first place: it’s clearly a good fit for a chatbot!

It can really pay to design your prototype with production in mind.

And since Elixir doesn’t hamper our development efforts (on the contrary: if anything, it may have helped to speed things up), it’s not a compromise, but a win-win solution.

The rich ecosystem

Even though Elixir is a fairly young language (it was created in 2011, and version 1.0 was released in September of 2014), it already has a good and growing ecosystem of libraries that can be used to expand the out-of-the-box functionalities by using the included (simple and efficient) package manager.

One of the most interesting framework is Phoenix, which specializes in web communication. It features, among other things, transparent JSON serialization/deserialization leveraging pattern-matching on Elixir data structures, powerful HTML templating and soft-realtime support with its Channel technology. Setting up a REST api with Phoenix is actually quite trivial.

The Phoenix framework, along with a certain number of libraries, has proven quite useful in the development of our chatbot. It really helps to be able to rely on various robust extensions, instead of having to reinvent the wheel.

Versatile and efficient tooling

Elixir is supported by all major IDEs we’ve tried (we had mostly favorable first-hand experiences with Atom and Visual Studio Code): this include syntax-highlighting and even some level of code completion. The unit testing framework, ExUnit, works really well and was easily integrated with our CI (Continuous Integration). And the fact that the included Mix tool manages application compiling, testing and executing (while being easily extendable with personalized tasks) greatly helped to simplify our workflow.

We did encounter some problems with the structure of Umbrella projects (maybe the subject of another post?), but nothing major. The only thing we really miss is auto formatting, and it should be supported starting with Elixir 1.6 (we’re actually quite excited about this!).

All in all, Elixir and all the tooling that comes with it fit really well with our own work methodology, which is Agile based (we are currently in Kanban mode, to better reflect and frame our experimental, prototypal approach).

Extreme flexibility through metaprogramming

Just this point could take a whole article, but I have to mention it, at least briefly. Since Elixir lets you interact with the way code is represented internally (in the form of an abstract syntax tree, or AST), it is possible, using macros, to easily extend the language, or even implement completely new languages.

This is actually what we did to implement our very own dialog manager, in order to give us complete freedom over the way we can represent the dialog. And since we are still experimenting with that, the ability to quickly adapt our custom syntax to our evolving needs is not only a nice perk: it is a necessity.

Of course, Elixir is by no means a silver bullet. The language and the platform are not perfect, and simply using this technology is not a guarantee of success. Really, Elixir is just a tool, and even though it can do a lot, especially if placed in the right hands, it can never truly replace knowledge, skill and vision.