Converting Erlang code into Elixir

When you are new to any language, you probably want to run some existing code just to see how it works. Achieving success while trying new things is important, because it helps fueling your interest.

The number of code examples in Elixir is increasing, but sometimes you will have to read some Erlang code. Recently, I wanted to play a little bit with Cowboy HTTP Server, which is written in Erlang. The Cowboy repo has a lot of small examples presenting the features which is provided by it. When I tried to convert one of them to Elixir, it wasn’t as simple as I expected, since I’m not so familiarized with the language yet.

When converting, you may get into some misleading code that will not work as you expected at first. So, I’m going to present a transcoding of Cowboy WebSocket server example from Erlang to Elixir, so that you can learn some of the details that exists in the process of porting Erlang code into Elixir code.

This will not be a tutorial explaining how that Cowboy example works, it’s just about how to convert it to Elixir. Also, I’m not going to show how it could be done in idiomatic Elixir, the goal here is to translate Erlang code into Elixir the simplest way possible.

So let’s start!

Creating the project

Create a project called ws_cowboy with the following command:

mix new ws_cowboy
cd ws_cowboy

After that we are going to change/create 4 files:

  • mix.exs: declares the dependencies in the project and the application module to run
  • lib/ws_cowboy.ex: the application module that setups the cowboy routes and http server
  • lib/ws_handler.ex: handles a WebSocket request connection
  • lib/ws_supervisor.ex: supervisor for Cowboy server

Also, copy the whole priv directory from the Cowboy example to the project’s root dir.

The project definition

In the mix.exs file we are going to add the Cowboy dependency and also configure the module application, in this case WsCowboy.

defmodule WsCowboy.Mixfile do
  use Mix.Project

  def project do
    [app: :ws_cowboy,
     version: "0.0.1",
     elixir: "~> 1.0",
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type `mix help compile.app` for more information
  def application do
    [applications: [:logger, :cowboy],
     mod: {WsCowboy, []}]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type `mix help deps` for more examples and options
  defp deps do
    [{:cowboy, "~> 1.0.0"}]
  end
end

Configuring the HTTP application

Here is the transcode of websocket_app.erl file to the ws_cowboy.ex file:

defmodule WsCowboy do
  @behaviour :application

  def start(_type, _args) do
    dispatch = :cowboy_router.compile([
      {:_, [
        {"/", :cowboy_static, {:priv_file, :ws_cowboy, "index.html"}},
        {"/websocket", WsHandler, []},
        {"/static/[...]", :cowboy_static, {:priv_dir, :ws_cowboy, "static"}}
      ]}
    ])
    {:ok, _} = :cowboy.start_http(:http, 100, [{:port, 8080}],
                                  [{:env, [{:dispatch, dispatch}]}])
    WsSupervisor.start_link
  end

  def stop(_state) do
    :ok
  end
end

If you never read any Erlang code and you came from a language like Ruby, you might get confused on basic things. So, let’s go through some of the details of porting websocket_app.erl to the ws_cowboy.ex.

Erlang files represents a module, in Elixir it is the same thing, but in this case we use the defmodule macro. Erlang modules can be accessed using :<module_name>, so in this case we are defining that this module has the application behaviour.

Different from other languages like Ruby, lowercase names aren’t variables in Erlang, but atoms, while in Elixir atoms look the same as Ruby symbols. So, we scanned every lowercase names and replaced them with :<name>.

Upper case names in Erlang aren’t constant but variables, so we changed them to lowercase. It is good to do this in inverse order to avoid mixing variables and atoms.

During the process of converting this file, there was a line making the application not work and I spent some time trying to figure out what was wrong. In Ruby, 'foo' and "foo" are both strings, but in Elixir and Erlang they are different things. The single quote in Erlang is an atom (symbol), so the '_' line must be converted to :_ in Elixir. If you miss this little detail, unfortunately it will compile and run, but Cowboy will always return a 400 status code.

Except that, everything is pretty straightforward, the only detail is the :cowboy_static definition that you have to replace with your dir app name, in this case :ws_cowboy.

To transcode function calls, you just have to replace the : with ., like in Ruby.

Handling the WebSocket connection

You can read more about how Cowboy handles WebSocket here. Here’s the transcode of the file ws_handler.erl to ws_handler.ex:

defmodule WsHandler do
  @behaviour :cowboy_websocket_handler

  def init({:tcp, :http}, _req, _opts) do
    {:upgrade, :protocol, :cowboy_websocket}
  end

  def websocket_init(_transport_name, req, _opts) do
    :erlang.start_timer(1000, self(), "Hello!")
    {:ok, req, :undefined_state}
  end

  def websocket_handle({:text, msg}, req, state) do
    {:reply, {:text, "That's what she said! #{msg}"}, req, state}
  end

  def websocket_handle(_data, req, state) do
    {:ok, req, state}
  end

  def websocket_info({:timeout, _ref, msg}, req, state) do
    :erlang.start_timer(1000, self(), "How' you doin'?")
    {:reply, {:text, msg}, req, state}
  end

  def websocket_info(_info, req, state) do
    {:ok, req, state}
  end

  def websocket_terminate(_reason, _req, _state) do
    :ok
  end
end

Following the steps done in the previous file, there is no secret on this one. The only detail is that the Erlang version used binary notation for strings, but in Elixir you can use just "string" normally. Also, you can use string interpolation "That's what she said! #{msg}".

Writing the supervisor

Now there’s just one translation missing, from websocket_sup.erl to ws_supervisor.ex. Here we just used __MODULE__ instead of the Erlang ?MODULE:

defmodule WsSupervisor do
  @behaviour :supervisor

  def start_link do
    :supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
  end

  def init([]) do
    procs = []
    {:ok, {{:one_for_one, 10, 10}, procs}}
  end
end

Running your server

Running your server is pretty easy, just run the mix run --no-halt command and check http://127.0.0.1:8080 in your browser.

Conclusion

The goal of this post was to show an example of how to port Erlang code into Elixir code. The result we got is the simplest translation possible, it was not my idea to write idiomatic Elixir here. For example, in Elixir you would not use Erlang’s supervisor module, but rather the Elixir’s supervisor.

I hope you could get the picture of how it is to translate Erlang code into Elixir, how it’s not so hard and some of the details that you must pay attention while doing it.

If you are interesting in learning more about Elixir, check out the getting started page.

Do you had any issues when starting to play with Elixir that you cracked your head to figuring out why it didn’t work? Share your experience and doubts with us!

Subscribe to our blog

Comments are closed.