Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.2.0 has been published, a release that focuses on improving the language server and developer experience. It’s a big one both in terms of size and impact, so let’s take a look as what it includes.

Fault tolerant compilation

Gleam’s compiler has traditionally halted immediately when a compile time error was encountered, presenting the error to the programmer and requiring them to fix it before compilation can be attempted again.

The main advantage of this is that the programmer has just the first and most important error, rather than a mix of the real error and a bunch of red-herring cascading errors. On the other hand, if there’s multiple genuine errors the programmer can only see the first one, which may not be the one they want to work on, or the most understandable.

Even worse, this halting behaviour causes major issues for the Gleam Language Server (the engine that text editors like Neovim, Helix, Zed, and VS Code use to get IDE features for Gleam). The language server internally uses the compiler to build and analyse Gleam projects, so when the compiler halts with an error the language server is left without up-to-date information about the code. Without a successful build some language server features may not work, and the longer a project goes without successful compilation (such as during a large refactoring) the more drift there can be between the language server’s understanding of the code and the reality.

With this release when the compiler encounters an error during analysis it will move on to the next definition in the module, returning all of the errors along with updated code information once the whole module has been analysed. This has resulted in a dramatic improvement in the experience of using the Gleam language server, it being much more responsive and accurate in its feedback.

In future releases we will continue to improve the granularity of the fault tolerant compilation and we will also introduce fault tolerant parsing.

Thanks to Ameen Radwan and myself for this feature!

Language server imports improvements

This is a Gleam import statement:

import gleam/option.{type Option, Some, None}
//     ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^
//      the module     unqualified imports

The language server could previously autocomplete module names, but now it can also autocomplete types and values when importing them in the unqualified fashion.

You can also hover types and values in an import statement to see their documentation, and use go-to definition on them and the module itself to jump to where they defined in your project or its dependencies.

Single line pipelines

Gleam’s much loved |> pipe operator can be used to pipe the output from one function into the input of another, not dissimilar to | pipes in bash, etc. Gleam’s code formatter would always put each function of a sequence of pipes on individual lines, but now you can opt to put them all on one line, so long as altogether they are short enough to fit.

[1, 2, 3] |> list.map(int.to_string) |> string.join(with: "\n")

In addition you can also force the formatter to break a pipe on multiple lines by adding a line break. This:

[1, 2, 3] |> list.map(int.to_string)
// By putting a newline here I'm telling the formatter to split the pipeline
|> string.join(with: "\n")

Will turn into this:

[1, 2, 3]
|> list.map(int.to_string)
|> string.join(with: "\n")

Thank you to our formatter wizard Giacomo Cavalieri for this feature!

Improved error messages for use expressions

Gleam’s use expressions are very powerful and can be used to replicate many built-in features of other languages such as Go’s defer, procedural languages’ early returns, Haskell’s do-notation, and more. As an unusual and powerful feature it can be confusing at first, and that was made worse as any type errors from incorrect use expressions would be generic ones rather than being specific to use.

These errors have been greatly refined and we hope this will help folks learning the language and debugging their code. Here’s some specific examples:

This error message is used when the right-hand-side of <- in a use expression is not a function:

error: Type mismatch
  ┌─ /src/one/two.gleam:2:8
  │
2 │ use <- 123
  │        ^^^

In a use expression, there should be a function on the right hand side of
`<-`, but this value has type:

    Int

See: https://tour.gleam.run/advanced-features/use/

This error message is used when the right-hand-side of <- is a function, but it takes no arguments:

error: Incorrect arity
  ┌─ /src/one/two.gleam:3:8
  │
3 │ use <- func
  │        ^^^^ Expected no arguments, got 1

The function on the right of `<-` here takes no arguments.
But it has to take at least one argument, a callback function.

See: https://tour.gleam.run/advanced-features/use/

And this one is used when it takes arguments, but not the correct number of arguments:

error: Incorrect arity
  ┌─ /src/one/two.gleam:3:8
  │
3 │ use <- f(1, 2)
  │        ^^^^^^^ Expected 2 arguments, got 3

The function on the right of `<-` here takes 2 arguments.
All the arguments have already been supplied, so it cannot take the
`use` callback function as a final argument.

See: https://tour.gleam.run/advanced-features/use/

And more! Too many to list here. Thank you to Giacomo Cavalieri for these big improvements!

Type/value mix up feedback

One common mistake is to try and import a type as a value, or a value as a type. When an imported item is found not to exist the compiler will now check to see if there is a matching type or value and explain the mix-up.

error: Unknown module field
  ┌─ /src/one/two.gleam:1:19
  │
1 │ import gleam/one.{One}
  │                   ^^^ Did you mean `type One`?

`One` is only a type, it cannot be imported as a value.
error: Unknown module type
  ┌─ /src/one/two.gleam:1:19
  │
1 │ import gleam/two.{type Two}
  │                   ^^^^^^^^ Did you mean `Two`?

`Two` is only a value, it cannot be imported as a type.

Thank you Pi-Cla for this feature!

Assertion exhaustiveness checking

Gleam has a let assert syntax to apply a pattern to a value, extracting values contained within if the pattern matches, and crashing the program if it does not.

The compiler now performs exhaustiveness checking on these assertions to find patterns that are total and warn that the assert is redundant as a result.

warning: Redundant assertion
  ┌─ /home/lucy/src/app/src/app.gleam:4:7
  │
4 │   let assert x = get_name()
  │       ^^^^^^ You can remove this

This assertion is redundant since the pattern covers all possibilities.

Thank you Giacomo Cavalieri for this feature.

Catching common crash mistakes

Gleam has todo and panic expressions, both of which crash the program due to it being incomplete or in an invalid state respectively. The syntax looks like this:

panic as "The session is no longer valid, this should never happen!"

A common mistake is to try and give the message string using the function call syntax like so:

panic("The session is no longer valid, this should never happen!")

This is valid Gleam code, but it is not using that string as a message for the panic. The string is unreachable as the program panics before it is evaluated. A compiler warning is now emitted when code like this is found.

warning: Todo used as a function
  ┌─ /src/warning/wrn.gleam:2:16
  │
2 │           todo(1)
  │                ^

`todo` is not a function and will crash before it can do anything with
this argument.

Hint: if you want to display an error message you should write
`todo as "my error message"`
See: https://tour.gleam.run/advanced-features/todo/

Thank you Giacomo Cavalieri for this feature!

Invalid constant error message improvement

Gleam’s constants are limited to a subset of the language that be evaluated at compile time, and as such arbitrary functions cannot be called within them. Previously if you tried to call a function you would get a somewhat confusing parse error, but with this version it’ll emit a much more helpful error message.

error: Syntax error
  ┌─ /src/parse/error.gleam:3:18
  │
3 │ const wib: Int = wibble(1, "wobble")
  │                  ^^^^^^^ Functions can only be called within other functions

Thank you Nino Annighoefer for this improvement!

Unreachable code detection

Code that comes after a panic expression is unreachable and will never be evaluated as the program will crash before it is reached. The compiler will now warn in these situations to catch these mistakes.

For example, this code will emit this warning:

pub fn main() {
  panic
  my_app.run()
}
warning: Unreachable code
  ┌─ /src/warning/wrn.gleam:3:11
  │
3 │    my_app.run()
  │    ^^^^^^^^^^^^

This code is unreachable because it comes after a `panic`.

Thank you Giacomo Cavalieri for this feature!

Further Hex integration

Gleam is part of the Erlang ecosystem, and as such it uses the Hex package manager.

The gleam hex revert has been added to the Gleam binary. It can be used to unpublish a package release within the first 24 hours of it being published. After 24 hours Hex package releases become immutable and cannot be removed.

Additionally the error message that is emitted when you attempt to publish a package that has already been published has been improved:

error: Version already published

Version v1.0.0 has already been published.
This release has been recently published so you can replace it
or you can publish it using a different version number

Hint: Please add the --replace flag if you want to replace the release.

Thank you Pi-Cla for these features!

Erlang module collision prevention

The Erlang virtual machine supports loading new versions of existing module, enabling an application to be upgraded while it is still running. One unfortunate consequence of this is that if you accidentally reuse a module name loading it into the VM will cause the application to be upgraded to the new version, causing confusing errors as functions that no longer exist are called and crash.

The Gleam build tool would return an error and refuse to compile Gleam modules that would overwrite each other, but with this version it will also emit an error if a Gleam module would overwrite a built-in Erlang/OTP module.

error: Erlang module name collision

The module `src/code.gleam` compiles to an Erlang module named `code`.

By default Erlang includes a module with the same name so if we were to
compile and load your module it would overwrite the Erlang one, potentially
causing confusing errors and crashes.

Hint: Rename this module and try again.

Better build tool errors

A helpful error message is now shown if the manifest.toml file has become invalid, perhaps due to a git rebase or accidental edit.

error: Corrupt manifest.toml

The `manifest.toml` file is corrupt.

Hint: Please run `gleam update` to fix it.

Previously when no package versions could be found that satisfy the project’s requirements, a very cryptic message would printed that detailed all the constraints from all the versions that were incompatible. This was overwhelming and practically impossible to read, so it has been replaced with a much simpler message.

error: Dependency resolution failed

An error occurred while determining what dependency packages and
versions should be downloaded.
The error from the version resolver library was:

Unable to find compatible versions for the version constraints in your
gleam.toml. The conflicting packages are:

- hellogleam
- lustre_dev_tools
- glint

Thank you zahash for these improvements!

Redundant pattern matching warnings

Gleam’s case expressons support pattern matching on multiple values at the same time. This is uncommon so people used to other languages may habitually wrap multiple values in a tuple or a list to match on them. The compiler will now warn when this is done:

warning: Redundant list
  ┌─ /src/warning/wrn.gleam:2:14
  │
2 │         case [1, 2] {
  │              ^^^^^^ You can remove this list wrapper

Instead of building a list and matching on it, you can match on its
contents directly.
A case expression can take multiple subjects separated by commas like this:

    case one_subject, another_subject {
      _, _ -> todo
    }

See: https://tour.gleam.run/flow-control/multiple-subjects/

Thank you Giacomo Cavalieri for this feature.

Redundant pattern matching auto-fix

And further still, if you are using the Gleam language server then a code action is offered to automatically fix redundant tuple wrappers. Place your cursor on the tuple and select this language action and the language server will remove the tuple from the values as well as from all patterns in the case expression.

case #(x, y) {
  #(1, 2) -> 0
  #(_, _) -> 1
}

Is rewritten to:

case x, y {
  1, 2 -> 0
  _, _ -> 1
}

Thank you Nicky Lim for this code action, and for laying the foundation for other code actions like this in future!

A helping hand for JavaScript programmers

JavaScript programmers sometimes type === by mistake in their Gleam code. We have an error message for that too now:

error: Syntax error
  ┌─ /src/parse/error.gleam:4:37
  │
4 │   [1,2,3] |> list.filter(fn (a) { a === 3 })
  │                                     ^^^ Did you mean `==`?

Gleam uses `==` to check for equality between two values.
See: https://tour.gleam.run/basics/equality

Thank you Rabin Gaire for this feature!

And the rest

If you’d like to see all the changes for this release, including all the bug fixes, check out the changelog in the git repository.

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!

Alternatively consider using our referral link for CodeCrafters, who have recently launched a course on implementing Redis in Gleam. Use your training budget to learn and to support Gleam too!

Thanks for reading, I hope you have fun with Gleam! 💜

Try Gleam