Using Gleam packages in an Elixir project is not as simple as adding {:lib, "1.2.3"}
to your
mix.exs
, but it's pretty close. There are two issues preventing this simple usage:
- Elixir's Mix doesn't know how to compile a Gleam package, which does not have any build setup. It's
literally a bunch of Erlang files in a folder. I'm not sure if there is an issue about this.
- Gleam writes dev dependencies into the built
app.src
file, which causes the app to crash on startup,
as the dev dependencies aren't there. Gleam issue #3035
deals with this matter.
To circumvent these issues, add your Gleam dependency to mix.exs
like this:
{:scriptorium, "~> 2.0.0", app: false, manager: :rebar3}
The first option prevents reading the app file of the dependency entirely. It seems to work for
libraries, but I'm not sure of its effects on Gleam projects with startable apps. I believe they won't be
automatically started at least. Most Gleam projects at this point are libraries or are explicitly
started, so this is likely not an issue for you.
The second tells Mix to use Rebar to build the package. This seems to work even for a plain "Erlang files
in a folder" type package that Gleam generates.
You can read more about these options in Mix's documentation.
Once you've gotten your dependency installed, remember that the calling syntax is different from a
typical Elixir module. Gleam modules are named according to their path with @
as a separator. For
example, to call parse_header
from scriptorium/parser/common
, use the following:
:scriptorium@parser@common.parse_header("...")
Also note that Gleam custom types map to tuples in Elixir, and more specifically
records. In fact you can define a record on the Elixir side to
match your Gleam types and operate on them. For example, here's a record from one of my early mixed
Elixir/Gleam projects, GeoTherminator:
defmodule GeoTherminator.PumpAPI.Device do
require Record
Record.defrecord(:record, :device, [
:id,
:device_id,
:is_online,
:last_online,
:created_when,
:mac_address,
:name,
:model,
:retailer_access
])
# Create idiomatic Elixir type as an alias for Gleam side type
@type t :: :pump_api@device.device()
end
Now we can use GeoTherminator.PumpAPI.Device.t()
in typespecs, and calls like
GeoTherminator.PumpAPI.Device.record(my_device, :id)
and
GeoTherminator.PumpAPI.Device.record(my_device, name: "My Device")
to operate on the type in Elixir
land.