Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v0.30.0 has been released, let’s take a look at what’s new.

Local dependencies

Gleam’s build tool has always had support for Hex, the package repository for the Erlang ecosystem. It now additionally supports local dependencies, allowing you to depend on other Gleam projects without pushing them to the repository.

# gleam.toml
name = "my_project"

[dependencies]
gleam_stdlib = "~> 0.30"
my_other_project = { path = "../my_other_project" }

You may want to use local dependencies when testing your project before publishing, when working on multiple related packages, or to enforce a certain dependency structure between parts of your codebase, such as ensuring a domain layer does not depend on a web framework.

One way I will be using this feature is to have a package of shared types and functions that I can use in both the Gleam Erlang backend and the Gleam JavaScript frontend of my web applications.

Thank you Miles Silberling-Cook for this feature!

Enhanced externals

Gleam can without any overhead import and call functions written in other languages on the runtime, such as Erlang, Elixir, JavaScript, and TypeScript.

If I wanted to define a public function that used FFI (foreign function interface) to halt the runtime I could do it like this:

/// Halt the runtime with the given exit code.
pub fn halt(code: Int) -> Nil {
  native_halt(code)
}

if erlang {
  external fn native_halt(Int) -> Nil =
    "erlang" "halt"
}

if javascript {
  external fn native_halt(Int) -> Nil =
    "./my_javascript_module.mjs" "halt"
}

Here a public function with a documentation comment calls a private function, and conditional compilation is used to define the private function differently depending on the target runtime.

I think you’ll agree that it is quite verbose and repetitive; of the 14 lines of code in this example only 4 are meaningful.

Conditional compilation gives the programmer little in the way of guidance on how to structure their code, so other approaches can be found today in published Gleam packages:

Some packages only support a single target runtime, as such they may omit the conditional compilation entirely, documenting the supported target in their README. These packages will not work on other runtimes, and may fail to compile with a cryptic message.

Other packages omit the non-conditional facade function entirely, instead making the two conditional functions public. This approach is more concise but without any link between the two different implementations there can be drift, with types and functions either intentionally or unintentionally differing.

With Gleam v0.30.0 the FFI has been redesigned to address all these issues. The above example is now written like this:

/// Halt the runtime with the given exit code.
@external(erlang, "erlang", "halt")
@external(javascript, "./my_javascript_module.mjs", "halt")
pub fn halt(code: Int) -> Nil

This is much more concise, and the single function header ensures the various targets are kept in sync.

What’s more the compiler can now always infer what targets a function is implemented for, which in future we can use for dead code elimination and to show supported targets for packages on the Gleam package index website.

Panic! Todo!

Gleam has todo and panic keywords that can be used to crash the program when code is reached that is either unfinished or should never be reached. If you wanted to give a custom message to that crash for todo you would have written it like this:

pub fn main() {
  todo("I really need to finish this...")
}

This syntax resulted in some confusion where people would think that these keywords are functions, and try to assign them to values. To make it clearer the syntax for providing a custom message has been changed to this:

pub fn main() {
  todo as "I really need to finish this..."
}

This syntax may be extended in future for assertions and other error messages.

Any order aliases

Previously type aliases could only refer to aliases defined earlier in the same module. This restriction has been lifted, so this mundane looking code will now compile.

pub type Bill = List(#(String, Euro))

pub type Euro = Int

Build tool improvements

Gleam packages can now specify the minimum version of Gleam required to build them.

# gleam.toml
name = "my_project"
gleam = ">= 0.30.0"

If a user attempts to build a package with a version that doesn’t meet the specified version constraint an error will be emitted.

And lastly, the gleam remove command has been added to remove dependencies from a package, to complement the existing gleam add command.

Thank you Miles Silberling-Cook and Mateusz Bil for these features!

Thanks

Gleam is made possible by the support of all the kind people and companies who have very generously sponsored or contributed to the project. Thank you all!

If you like Gleam consider sponsoring or asking your employer to sponsor Gleam development. I work full time on Gleam and your kind sponsorship is how I pay my bills!

Thanks for reading! Happy hacking! 💜