Gleam is a type-safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.10.0 has been published. Here's what's new:

Project-wide call reference graph

The compiler has been upgraded to retain more information about the types and values and how they reference each other in Gleam programs. With this information the language server (which is included within the gleam binary) now provides the "find references" feature, so you can find everywhere in your project where a type or value is used.

The additional information has also been used to improve the "rename" language server feature. Previously only module-local types and values could be renamed, but now renaming can be performed across all modules in a project.

Lastly the compiler's unused-code detection has been greatly improved, fixing some situations in which it would fail to detect it all previously.

Thank you to Surya Rose for implementing this! It is probably our most anticipated addition within the community!

Improved exhaustiveness analysis

Gleam's compiler performs exhaustiveness analysis, ensuring that all possible variants of the value are handled in flow control, and that there are no redundant cases which are not reachable due to previous cases.

Giacomo Cavalieri has spent the time since the previous release studying the academic literature on the complex subject of exhaustiveness checking to bring a variety of improvements to our implementation. The first of these changes has made it into this release: string pattern analysis. Previously only rudamentary analysis was performed on strings, and unreachable string patterns could not be detected.

case a_string {
  "Hello, " <> name -> name
  "Hello, Jak" -> "Jak"
  _ -> "Stranger"
}

In this code the first pattern matches all the values that the second pattern matches, so the second pattern is not reachable. The compiler now emits this warning:

warning: Unreachable case clause
  ┌─ /src/greet.gleam:7:5
  │
7 │     "Hello, Jak" -> "Jak"
  │     ^^^^^^^^^^^^^^^^^^^^^

This case clause cannot be reached as a previous clause matches the same
values.

Hint: It can be safely removed.

Thank you Giacomo! Excellent work!

Alongside that the compiler now emits a warning when using let assert to assert a value whose variant has already been inferred, such as in this code:

let e = Error("Some error")
let assert Ok(_) = e // warning: Pattern will never match

Thank you Surya Rose!

Operator analysis improvements

Gleam's compiler is fault tolerant, meaning that when an error is encountered in the code it does not stop immediately, instead it shows the error to the programmer, makes a best-effort attempts to recover and continue analysing the rest of the code. This behaviour is vital for the langauge server to have information about the code even when it's in an invalid state.

The analysis of binary operators such as + and == have been improved so that both sides will be checked even in the presence errors. This improves language server information in some situations, and it also enables improved error messages.

Gleam doesn't have operator overloading or interfaces, so each operator works with a specific type. For example + is only for adding ints, and string concatenation is done with the <> operator. Previously the error message for the wrong operator would point to the value and say it is of the wrong type, but now it points to the operator itself and tells you what the correct operator would be:

error: Type mismatch
  ┌─ /src/wibble.gleam:2:13
  │
2 │   "Hello, " + "Lucy"
  │             ^ Use <> instead

The + operator can only be used on Ints.
To join two strings together you can use the <> operator.

The language server willl also offer a code action to fix the error for you in your editor.

pub fn main() {
  "Hello, " + "Jak"
//^^^^^^^^^^^^^^^^^ When hovering anywhere over here
}

Triggering the code action would fix the compilation error by using the correct <> operator instead of +:

pub fn main() {
  "Hello, " <> "Jak"
}

Thank you Giacomo Cavalieri!

Bit array improvements

Gleam's bit array literal syntax provides a powerful and convenient way to construct and parse binary data.

It is fully supported when targeting the Erlang virtual machine, but some bit array literals were not fully supported when targeting JavaScript. Surya Rose has added support for the unit option to control the units of the size option, and Richard Viney has added support for 16-bit floats.

Giacomo Cavalieri has made it possible to omit the :float option when used with float literals, as the intention is unambiguous. These two literals are now equivalent, while previously the former would be a type error due to lacking sufficient annotations.

<<1.11>>
<<1.11:float>>

Thank you Surya, Richard, and Giacomo!

JavaScript codegen performance improvements

Surya Rose has improved the JavaScript code generation to make the code run faster. Where possible the compiler restructures code to no longer use JavaScript "immediately invoked function expressions", removing the overhead of allocating and then calling these functions.

This is the first of a series of JavaScript performance improvements we are currently working on, and our initial benchmarks show they will be impactful, improving the performance of all Gleam programs that compile to JavaScript without any modification to the programs themeselves.

Thank you Surya!

Cross platform deployment artefacts

BEAM bytecode is portable. You can compile your Erlang, Elixir, or Gleam program on one machine and run it on another with a different operating system and processor architecture.

One annoyance if you were doing this with Gleam was that the gleam export erlang-shipment command would try to be clever and give you an entrypoint script for the operating system you are currently using, so if you compiled on Windows to run on Linux then you would be missing the POSIX shell entrypoint script.

Greg Burri has resolved this problem by having the build tool include all entrypoint scripts, regardless of the operating system used to build the artefact. Thank you Greg!

Package information export

Rodrigo Álvarez has created the gleam export package-information command, which will write information about a Gleam package to file in JSON format. This information may be useful to other build tools that wish to compile Gleam code, and Rodrigo is exploring adding support for Gleam dependencies to Elixir's Mix build tool.

Thank you Rodrigo! This will be especially exciting for Elixir programmers as many folks use Gleam and Elixir together.

Fill unused fields code action

The language server can now offer a code action to replace a .. in a pattern with all the fields that it was being used to ignore. For example triggering the code action on this code:

pub type Pokemon {
  Pokemon(id: Int, name: String, moves: List(String))
}

pub fn main() {
  let Pokemon(..) = todo
  //          ^ If you put your cursor here
}

Would result in the code being edited like so:

pub type Pokemon {
  Pokemon(id: Int, name: String, moves: List(String))
}

pub fn main() {
  let Pokemon(id:, name:, moves:) = todo
}

Thank you Giacomo Cavalieri!

Improved JSON encoder generation code action

The function generated by the "Generate JSON encoder" code action will now fail to compile if the type has new fields added. The motivation for this may not be immediately clear, so let's dig into that a little.

Gleam's development philosophy is to use static analysis, editor tooling, and other forms of computer assistance to reduce the mental load for the programmer as much as possible. With the previous design if the data type definition was edited the programmer would have to remember about the existance of the JSON encoder, making it possible to forget and accidentally introduce a bug. With the new behaviour this mistake is no longer possible.

Here is an example of a type and the encoder function that the language server will now generate for it. Note the new pattern matching with let.

type Person {
  Person(name: String, age: Int)
}

fn encode_person(person: Person) -> json.Json {
  let Person(name:, age:) = person
  json.object([
    #("name", json.string(name)),
    #("age", json.int(age)),
  ])
}

Thank you Surya Rose!

Remove echo code action

Gleam has the echo keyword for debug printing. It's convenient for quickly understanding runtime behaviour of code, but it should never be included in production code and the build tool will refuse to publish code that uses it.

The language server now offers a code action to remove all echos in a module. For example:

pub fn main() {
  echo 1 + 2
}

Triggering the code action would remove the echo:

pub fn main() {
  1 + 2
}

Thank you Giacomo Cavalieri!

Extract constant code action

The language server now offers the option to lift non-dynamic expressions into constants. If the code action is run on the list literal here:

const lucy = "Lucy"

const nubi = "Nubi"

pub fn main() {
  list.each([lucy, nubi, "Biffy"], io.println)
}

The code is updated like so:

const lucy = "Lucy"

const nubi = "Nubi"

const values = [lucy, nubi, "Biffy"]

pub fn main() {
  list.each(values, io.println)
}

Thank you Matias Carlander!

Wrap in block code action

The language server will offer to wrap assignment or case clause values in blocks. This is is a convenience for when adding more expressions to some existing code that previous only had one expression, making everyday Gleam editing that little bit easier.

For example, with this code:

pub fn f(pokemon_type: PokemonType) {
  case pokemon_type {
    Water -> soak()
    //       ^^^^^^ Cursor within this expression
    Fire -> burn()
  }
}

Triggering the code action results in the code being updated like so:

pub fn f(pokemon_type: PokemonType) {
  case pokemon_type {
    Water -> {
      soak()
    }
    Fire -> burn()
  }
}

Thank you Matias Carlander!

Security and compliance

Gleam's container images now contain Software Bill of Materials (SBoM) and Supply-chain Levels for Software Artifacts (SLSA) Provenance information.

This will greatly help with security audits and compliance of software written with Gleam, and is part of an ongoing effort to evidence production readiness and promote Gleam adoption within enterprise. This is well timed, as Gleam has just featured in the Thoughtworks technology radar for the first time!

Thank you to Jonatan Männchen and the Erlang Ecosystem Foundation for this and their ongoing support of Gleam and Gleam companies.

And the rest

And thank you to the bug fixers and experience polishers: Alexander Keleschovsky, Drew Olson, Giacomo Cavalieri, Louis Pilfold, Matias Carlander, Sakari Bergen, Sam Zanca, Samuel Cristobal, and Surya Rose.

For full details of the many fixes and improvements they've implemented see the changelog.

A call for support

Gleam is not owned by a corporation; instead it is entirely supported by sponsors, most of which contribute between $5 and $20 USD per month, and Gleam is my sole source of income.

We have made great progress towards our goal of being able to appropriately pay the core team members, but we still have further to go. Please consider supporting the project or core team members Giacomo Cavalieri and Surya Rose on GitHub Sponsors.

Thank you to all our sponsors! And especially our top sponsor: Lambda.

Try Gleam