Blog Cover

How to use erlang applications in elixir and how to translate them to elixir code.

Author profile image
Aitor Alonso

Oct 27, 2024

4 min read

As you might already know, Elixir is built on top of the Erlang Virtual Machine (BEAM). This means that Elixir and Erlang share the same underlying runtime system, allowing for seamless interoperability between the two languages. Elixir code compiles to Erlang bytecode, which runs on the BEAM, making it possible to use Erlang libraries and modules directly within Elixir projects.

This interoperability allows Elixir developers to leverage the vast ecosystem of Erlang libraries and applications without the need for translation or rewriting. You can directly use existing Erlang applications in your Elixir code by calling their functions using the atom syntax. Let's see how it works.

Using Erlang applications directly in Elixir

To call an Erlang function from Elixir, you use the syntax :module.function(args). The colon before the module name tells Elixir that you're referring to an Erlang module. This works out of the box for modules included in the Erlang standard library.

For example, if you want to use the base64 module from Erlang's standard library, you can do:

encoded = :base64.encode("Hello, World!")
decoded = :base64.decode(encoded)

Now, let's see how to use an Erlang application that is not included in the Erlang standard library. Erlang applications are typically started automatically when your Elixir application starts, as long as they are listed in your mix.exs file under the :extra_applications key. For example:

def application do
  [
    extra_applications: [:logger, :crypto]
  ]
end

This ensures that the Erlang crypto application is started along with your Elixir application. After that, you can use its functions in your Elixir code. Here's an example of using the crypto module to generate a random string:

# Generate 16 random bytes
random_bytes = :crypto.strong_rand_bytes(16)

# Convert the bytes to a hexadecimal string
random_string = Base.encode16(random_bytes, case: :lower)

IO.puts("Random string: #{random_string}")

This code generates a random 16-byte string and encodes it as a lowercase hexadecimal string. The :crypto.strong_rand_bytes/1 function is an Erlang function from the crypto application, called directly from Elixir code.

But what if you want to directly translate Erlang code to Elixir code? Well, Elixir and Erlang are not 100% compatible, but they are very similar, so you can translate Erlang code to Elixir with a few changes. Let's see how it works.

Translating Erlang code to Elixir code

Translating Erlang code to Elixir is easy as long as you keep in mind the following things when transcoding Erlang to Elixir:

  • Erlang atoms start with a lowercase letter, whereas Elixir atoms start with a colon (:). For example, ok in Erlang becomes :ok in Elixir.
  • Erlang variables start with an uppercase letter, whereas Elixir variables start with a lowercase letter. For example, Socket in Erlang becomes socket in Elixir.
  • Erlang modules are always referenced as atoms. For example, gen_tcp in Erlang becomes :gen_tcp in Elixir.
  • Function calls in Erlang use a colon (:) whereas function calls in Elixir always use a dot (.). For example, gen_tcp:listen in Erlang becomes :gen_tcp.listen in Elixir. (Replace the colon with a dot.)
  • Last, but no least, it's important to note that Erlang strings aren't the same as Elixir strings. In Erlang, a double-quoted string is a list of characters whereas in Elixir a double-quoted string is a sequence of bytes (a binary). Thus, double-quoted Erlang and Elixir strings aren't compatible. So if an Erlang function takes a string argument, you can't pass it an Elixir string. Instead, Elixir has a character list which you can create using single-quotes rather than double-quotes. For example, 'hello' is a list of characters that's compatible with the Erlang string "hello".

Let's see how it works with an example. Let's translate the server example from the docs for generic TCP/IP server in Erlang (gen_tcp) to Elixir code.

We start with the following Erlang code, that defines the server function:

server() ->
    {ok, LSock} = gen_tcp:listen(5678, [binary, {packet, 0}, {active, false}]),
    {ok, Sock} = gen_tcp:accept(LSock),
    {ok, Bin} = gen_tcp:recv(Sock, 0),
    ok = gen_tcp:close(Sock),
    ok = gen_tcp:close(LSock),
    Bin.

Now, translated to Elixir code, it looks like this:

def server() do
    {:ok, lsock} = :gen_tcp.listen(5678, [:binary, packet: 0, active: false])
    {:ok, sock} = :gen_tcp.accept(lsock)
    {:ok, bin} = :gen_tcp.recv(sock, 0)
    :ok = :gen_tcp.close(sock)
    :ok = :gen_tcp.close(lsock)
    bin
end

Some notes about the changes made:

  • When translating the first function call to :gen_tcp.listen, the arguments received by that function are translated as [:binary, {:packet, 0}, {:active, false}]), which can be simplified to a Keyword list in elixir, as Keyword list are just list of tuples.
  • Erlang instructions always ends with a comma. Think of them as the semicolon (;) in other languages such as C or Java. Since Elixir doesn't use any character to separate instructions in different lines you need to remove them.
  • The end of a function in Erlang is marked by a dot (.). In Elixir, it's marked by the end keyword.

Conclusion

And that's it! You now know how to use Erlang applications in your Elixir codebase, and also how to translate Erlang code to Elixir code. You can copy paste the above code in your Elixir project and run it.


I hope my article has helped you, or at least, that you have enjoyed reading it. I do this for fun and I don't need money to keep the blog running. However, if you'd like to show your gratitude, you can pay for my next coffee(s) with a one-time donation of just $1.00. Thank you!

No by AICC-BY 4.0