Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v0.33.0 has been published, a release that contains some features we in the community have been looking forward to for a long time! Let’s take a look.

Exhaustive exhaustiveness checking

Gleam is designed to make writing and running production software less stressful. One way it does this is by preventing runtime errors that would have you scrambling to fix bugs in the middle of the night. If you don’t use assertions then Gleam code will never throw an exception, outside of hardware problems such as memory exhaustion.

Or that was the idea. There was one place where Gleam was not living up to this promise: pattern matching.

Pattern matching with a case expression is how flow control is done in Gleam. A series of clauses are written, and the first clause with a pattern that matches the input gets run. It’s similar to a switch statement in other languages, but with some extra functionality.

pub fn describe(list: List(t)) -> String {
  case list {
    [] -> "Empty list"
    [_] -> "Singleton list"
    [_, _] -> "Two element list"
    _ -> "Long list"
  }
}

If no pattern matches the input then a runtime error is thrown. This is not what we want! To prevent this the Gleam compiler implements exhaustiveness checking. If there are missing patterns then it will emit an error at compile time, prompting the programmer to fix the code.

Unfortunately the implementation of exhaustiveness checking was, perhaps amusingly, inexhaustive. It did not work for more complex patterns.

With this release Gleam implements the algorithm described in the paper How to compile pattern matching by Jules Jacobs, with help from Yorick Peterse and his Pattern matching in Rust project, and as such it will now catch all missing patterns. Thank you Jules and Yorick!

Prettier printing

Gleam has a code formatter that will reformat your code to the canonical Gleam style, saving the keypresses you’d spend formatting your code, and freeing you from debates about exactly where to put those curly braces.

The formatter implements the algorithm described in the paper Strictly Pretty by Christian Lindig. We’ve been very happy with it, but there was a few places in which the format we supported was not exactly what we wanted.

let discounts =
  list.map(
    checkout_line_items,
    fn(item) { calculate_discount(item, discount_codes) },
  )

With this release the formatter has gained the “next break fits” extension found in Elixir’s pretty printer, which we now use to format the above code like so:

let discounts =
  list.map(checkout_line_items, fn(item) {
    calculate_discount(item, discount_codes)
  })

This new format is especially impactful for code with sophisticated DSLs, such as the web application framework Lustre.

//
// Before
//
html.div(
  [],
  [
    stack.of(
      html.nav, 
      [stack.tight()],
      [
        html.h3([], [text("Guides")]),
        html.a(
          [href("/guides/wisp")],
          [text("Integrating Lustre with Wisp framework")],
        ),
      ]
    ),
  ],
)

//
// After
//
html.div([], [
  stack.of(html.nav, [stack.tight()], [
    html.h3([], [text("Guides")]),
    html.a([href("/guides/wisp")], [
      text("Integrating Lustre with Wisp"),
    ]),
  ]),
])

Thank you to Giacomo Cavalieri for this feature!

Additionally, the gleam format command will ignore build directories even outside of git repositories. Thank you Alessandro Scandone.

Encouraging semantic versioning

Gleam’s package manager uses semantic versioning and the Pubgrub algorithm to pick versions of each package that are compatible with each other. This helps avoid dependency headaches, but it only works if package authors follow semantic versioning.

Developers often have a habit of using 0.x.x versions instead of following semantic versioning, undermining the work the package manager does to avoid unexpected breakages. If you’re publishing a package then it’s worth taking versioning seriously to give your users the best experience, even if you intend to do more work on the package in future.

To encourage moving past version zero Gleam now creates new projects at 1.0.0, and asks for confirmation when publishing a version zero package. It will also ask for confirmation if you try to publish a package with the name prefix gleam_, which implies the package is maintained by the Gleam core team.

In future Gleam may have additional publishing features, such as analysing the difference between the last published version and the current version to suggest an appropriate new semantic version number, as the Elm package manager does.

Web Assembly API revamp

The Gleam compiler can be compiled to WASM, allowing it to be used in the browser or anywhere else WASM can be used. The JavaScript bindings to this API have been improved, given support for multiple projects, and now expose warnings as well as errors.

const compiler = await import("/compiler/gleam_wasm.js");
await compiler.default();
compiler.initialise_panic_hook();

compiler.write_module(0, "mod", "pub fn add(a, b) { a + b }");
compiler.compile_package(0, "javascript")
compiler.read_compiled_javascript(0, "mod")
// This returns:
// "export function add(a, b) {\n  return a + b;\n }\n"

This new API is still a little low-level. In future we may have a more sophisticated one, if that would be useful to folks. Currently this API is being used to create a new in-browser interactive playground and tutorial for Gleam, which we hope to release in January.

Language additions

The \u{...} syntax can now be used in strings to specify unicode codepoints by their hexadecimal number. Thank you F. Schwalbe and Adi Salimgereev.

The panic as "..." and todo as "..." syntaxes now accept any expression that evaluates to a string, rather than just a string literal. Thank you Om Prakaash.

The words auto, delegate, derive, else, implement, macro, and test are now reserved for future use. This is done as we soon to hope to release a version 1.0.0 of Gleam, after which point we will be committed to not breaking backwards compatibility. Reserving these words now will allow us to use them for new language features in the future.

The bool negation ! operator can now be used in clause guard expressions.

The deprecated bit string, ambiguous type import, and JavaScript prelude inspect features have been removed.

Build tool additions

The gleam export erlang-shipment command, used to create an artefact suitable for deployment on a server, now supports Windows. Thank you bun.

It is possible for Elixir and Erlang packages to have a different name according to the virtual machine than to the package manager. These packages are now supported by the build tool and can be added as dependencies to Gleam projects.

The search bar in generated HTML documentation has been improved, making better suggestions for partial matches. Thank you F. Schwalbe.

The gleam run and gleam test commands gain the -t flag, which is an alias of the –target flag. Thank you shayan javani.

The short forms of js and erl can now be used as values for the --target flag on all commands that accept it. Thank you Mani Sundararajan.

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! 💜