How to use erlang applications in elixir and how to translate them to elixir code.
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 becomessocket
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 theend
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.