This post is part of a series of four, this post, A Rebar3 project using Gleam, Gleam calling JS and v.v., and Gleam in the browser.
This post looks at Gleam code calling Erlang, and Erlang code calling Gleam.
Gleam FFI to Erlang
The first target of Gleam was Erlang. It should come as no surprise that the interoperability of Gleam and Erlang is super smooth.
Let's get started!
gleam new happy
cd happy
To let Gleam call into Erlang, we use the Foreign Function Interface, commonly abbreviated to 'ffi'.
Add a line @external(erlang, "module", "function")
above a function declaration, with types, and omit the implementation.
Edit src/happy.gleam
so it contains:
import gleam/io
pub fn main() {
[1, 2, 3]
|> erlang_reverse_ints
|> io.debug
}
@external(erlang, "lists", "reverse")
pub fn erlang_reverse_ints(list: List(Int)) -> List(Int)
We are literally calling fun lists:reverse/1
.
This Erlang function accepts a list with any, mixed types of elements.
Gleam is typed, so we must specify the type of all arguments we are passing to the Erlang function,
and the type of the value that it returns.
Gleam types map directly to Erlang types, which makes calling, well, straightforward.
There is a list of type mappings for your reference.
Let's run it with gleam run
, this will show us [3, 2, 1]
.
! Note that when during run-time, the Erlang function returns a value of a different type, you are on your own. The type checker of Gleam is compile-time, not run-time.
To tap into the dynamic types of Erlang a bit more, we can specify that we pass a list of any type, and we will get a list of the same type:
import gleam/io
pub fn main() {
reverse_and_debug([1, 2, 3])
reverse_and_debug(["hello", "world"])
}
pub fn reverse_and_debug(list: List(a)) -> List(a) {
list
|> erlang_reverse
|> io.debug
}
@external(erlang, "lists", "reverse")
pub fn erlang_reverse(list: List(a)) -> List(a)
and then we get:
$ gleam run
Compiling
happy
Compiled
in 0.09s
Running
happy.main
[3, 2, 1]
["world", "hello"]
Erlang calling Gleam
The above code is compiled to Erlang code, I found it inbuild/dev/erlang/happy/_gleam_artefacts/happy.erl
:
-module(happy).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
-export([erlang_reverse/1, reverse_and_debug/1, main/0]).
-spec erlang_reverse(list(WR)) -> list(WR).
erlang_reverse(List) ->
lists:reverse(List).
-spec reverse_and_debug(list(WO)) -> list(WO).
reverse_and_debug(List) ->
_pipe = List,
_pipe@1 = lists:reverse(_pipe),
gleam@io:debug(_pipe@1).
-spec main() -> list(binary()).
main() ->
reverse_and_debug([1, 2, 3]),
reverse_and_debug([<<"hello"/utf8>>, <<"world"/utf8>>]).
which means you can simply call our Gleam function happy.reverse_and_debug([1, 2, 3])
from Erlang as happy:reverse_and_debug([1, 2, 3])
.
For completeness sake, you can see that our 'ffi' function is indeed literally calling the Erlang function.
[Added Mar 16] The easiest place to have Erlang code that can call Gleam code is directly in your ./src
folder. Gleam not only compiles .gleam
files, but also picks up .erl
files if the project target is Erlang.
How to get this compiled Gleam/Erlang code into your Erlang/rebar3 project is worth a blog post by itself.