Mixing Gleam & Elixir
Tags: elixir, gleam, mix
Published: 2020-03-20 12:33:26

---
title: Mixing Gleam & Elixir 
published: true
description: Adding Gleam to your Elixir project using Mix
tags: elixir, gleam, mix
---

**Update**: This was very much an initial exploration. There is now an official project with a [mix_gleam](https://github.com/gleam-lang/mix_gleam/) plugin. Please check that out!

----
At [Contact Stack](https://www.contact-stack.com), we've chosen Elixir & Phoenix as the core of our tech stack and we're happy with the choice. But... we do miss having a really good type system and a helpful compiler (like with [Elm](https://elm-lang.org/)) to guide us through development.

Elixir targets the BEAM virtual machine and there are few statically typed languages that target the BEAM as well. Here we're going to look into a simple set up for integrating [Gleam](https://gleam.run/) into your Elixir project using a [Mix Compiler Task](https://hexdocs.pm/mix/1.10.2/Mix.Task.Compiler.html).

Gleam is a statically typed functional programming language which uses a syntax that is more approachable to, say, Javascript, C or Java programmers than some ML flavoured languages. The Gleam compiler is written in Rust and outputs Erlang source files which can then be compiled to BEAM by the Erlang compiler.

Mix is a multi-functional build tool for Elixir projects. It can manage dependencies, run tests, execute tasks and compile your Elixir code. It also supports compiling erlang files by default and can be extended with Mix Compiler Tasks to compile other languages. We're going to create a Mix Compiler Task for Gleam so that Mix can use the Gleam compiler to build Gleam files.

You can try the steps below yourself and check out the demonstration repository: https://github.com/michaeljones/gleam-phoenix-mix. 

Here we go!

We're assuming you have the `gleam` compiler available in your environment to execute. Run this to check.
```
gleam --version
```
If you don't, then you can follow the [installation instructions](https://gleam.run/getting-started/installing-gleam.html) over in the Gleam docs.

Now we're going to go through setting up a [Phoenix](http://www.phoenixframework.org/) project from scratch. We won't go into the details but just so we have a clear base to work with.

Run the following commands to set up an empty phoenix project:
```
mkdir gleam-phoenix-mix
cd gleam-phoenix-mix
mix phx.new . --app my_app
mix ecto.create
cd assets 
npm install
cd ..
```

Now we need to create our mix compiler task. We put this in a `lib/mix/tasks/compile` directory to reflect the module name that we need to give it which is `Mix.Tasks.Compile.Gleam`. So we create our new directory:
```
mkdir -p lib/mix/tasks/compile/
```

And add the follow code in a file called `lib/mix/tasks/compile/gleam.ex`.
```elixir
defmodule Mix.Tasks.Compile.Gleam do
  use Mix.Task.Compiler

  def run(_args) do
    System.cmd("gleam", ["build"])
    :ok
  end
end
```
All this is really doing is running the gleam compiler directly with the 'build' argument to get it to build all the gleam files it can find. 

Run the elixir compiler so that we compile that mix task before we try to use it.
```
mix compile.elixir
```

Now to make gleam happy we need to set up a `.toml` config file for it. So we create a file called `gleam.toml` in the root of the project with the follow content.
```
name = "my_app"
```

Then we create a `src` directory for the gleam files. The Gleam compiler expects the gleam files to be in a `src` directory. This is different to elixir/mix which expects them in the `lib` directory by default. Elixir/mix can be configured but Gleam can't at the time of writing.
```
mkdir src
```

We then create our Gleam module by making a file called `src/hello_world.gleam` with the following code in it:
```
pub fn hello() {
  "Hello, from gleam!"
}
```

Next we make the following change to the `mix.exs` file to add our compiler task to the list of compilers being run when you do `mix compile` and to make sure mix's erlang compiler looks in the `gen` folder
for erlang files. The `gleam` compiler compiles `.gleam` files from the `src` folder into `.erl` files in the `gen` folder so we need the erlang compiler to find them there.
```diff
       elixir: "~> 1.5",
       elixirc_paths: elixirc_paths(Mix.env()),
+      erlc_paths: ["src", "gen"],
-      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
+      compilers: [:phoenix, :gettext, :gleam] ++ Mix.compilers(),
       start_permanent: Mix.env() == :prod,
       aliases: aliases(),
```
I assume that Mix runs the compilers from the start to the end of the list. So we want the `:gleam` entry to be before the `:erlang` entry that's in `Mix.compilers()` so that the Erlang compiler will run after the Gleam compile and compile the Erlang files that the Gleam compiler creates.

As a simple demonstration of interop between Elixir & the compiled gleam files, make the following change to `lib/my_app_web/controllers/page_controller.ex`:
```diff
 defmodule MyAppWeb.PageController do
   use MyAppWeb, :controller
 
   def index(conn, _params) do
-    render(conn, "index.html")
+    render(conn, "index.html", title: :hello_world.hello())
   end
 end
```
This works because Gleam modules are compiled to Erlang modules and, in Elixir, Erlang modules are accessible via the atom that is their name.

And then we make the following change to `lib/my_app_web/templates/page/index.html.eex`:
```diff
 <section class="phx-hero">
-  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
+  <h1><%= @title %></h1>
   <p>A productive web framework that<br/>does not compromise speed or maintainability.</p>
 </section>
```

Finally we can run:
```
mix compile
mix phx.server
```

And load `http://localhost:4000` in our browser to see "Hello, from gleam!" in the centre of the standard Phoenix welcome page.

Remember, if you're curious you can check out the final result at this demonstration repo on Github: https://github.com/michaeljones/gleam-phoenix-mix
## Final Thoughts

It is exciting to see that it isn't too hard to incorporate Gleam code into an Elixir project using Mix. It is great that Mix is built with this kind of extensibility in mind. There are definitely more questions to answer and rough edges to smooth out but it is exciting to have an avenue to using a strongly typed language on the BEAM.