Otter Nonsense

Gleam v0.11 released!

It’s time for another Gleam release! This time as well as taking a look at what’s new in the compiler, we’re going to take a look at what’s new in the wider Gleam ecosystem too.

Record update syntax

Previously when we wanted to update some of the fields on the record we would need to create a new instance and manually copy across the fields that have not changed.

pub fn level_up(pokemon: Pokemon) {
  let new_level = pokemon.level + 1
  let new_moves = moves_for(pokemon.species, new_level)
  Pokemon(
    level: new_level,
    moves: new_moves,
    name: pokemon.name,
    species: pokemon.species,
    item: pokemon.item,
  )
}

This is quite verbose! Worse, it’s error prone and annoying to type every time.

To remedy this problem Quinn Wilton has added the record update syntax to Gleam, so now we only need to specify the fields that change.

pub fn level_up(pokemon: Pokemon) {
  let new_level = pokemon.level + 1
  let new_moves = moves_for(pokemon.species, new_level)
  Pokemon(..pokemon, level: new_level, moves: new_moves)
}

All data in Gleam is immutable, so this syntax does not alter the values of the existing record, instead it creates a new record with the original values and the updated values.

Numbers

Tom Whatmore has added some new features to Gleam’s numbers.

There’s now syntaxes for binary, octal, and hexadecimal int literals.

// 4 ways of writing the int 15
let hexadecimal = 0xF
let decimal = 15
let octal = 0o17
let binary = 0b00001111

These might be handy in many situations such as when implementing a virtual machine or a game of tetris in Gleam.

Underscores can now be added to numbers, making larger numbers easier to read.

let billion = 1_000_000_000
let trillion_and_change = 1_000_000_000_000.57

Type holes

Type holes can be used to give partial annotations to values.

Here we’re saying that x is a List, but we’re not saying what type the list contains, leaving the compiler to infer it.

let x: List(_) = run()

This may be useful when adding annotations for documentation or to use a more restrictive type than would be inferred by the compiler, as you can leave out any parts of the annotation that are not important to you there.

Compiler improvements

The majority of the compiler work in this release has been improvements to existing features.

The formatter style has been improved in several ways, and the performance of the formatter has been improved. One popular change is to how it formats assignments that don’t fit on a single line.

// Before
assert Ok(
  tuple(user, session),
) = application_registry.get_session(request.user_id)

// Now
assert Ok(tuple(user, session)) =
  application_registry.get_session(request.user_id)

Much better!

Additionally several error messages have been improved to include more information on how to fix the problem, and a whole bunch of bug have been squashed. Please see the changelog for more detail.

HTTP

Outside of the compiler the big new thing in Gleam is the first release of Gleam HTTP. Inspired by existing libraries such as Ruby’s Rack, Elixir’s Raxx, and Haskell’s WAI, Gleam HTTP provides a single well-typed interface for creating web applications and clients while keeping the underlying HTTP client and server code modular.

In Gleam a HTTP service is a regular function that takes a HTTP request and returns a HTTP response, where request and response are types defined by the HTTP library.

Services

Here’s a simplistic service that echoes back any body sent to it.

import gleam/http.{Request}

pub fn echo_service(request: Request(_)) {
  http.response(200)
  |> http.set_resp_body(request.body)
}

As a normal Gleam function it is easy to test this service, no special test helpers or test server are required.

import gleam/should
import my_app

pub fn echo_test() {
  let response =
    http.default_req()
    |> http.set_req_body("Hello, world!")
    |> my_app.service

  response.status
  |> should.equal(200)

  response.body
  |> should.equal("Hello, world!")
}

Gleam’s HTTP library also provides middleware, which can be used to add additional functionality to a HTTP service.

import gleam/bit_builder
import gleam/http/middleware

pub fn service() {
  my_app.echo_service
  // Add a header
  |> middleware.prepend_resp_header("made-with", "Gleam")
  // Convert the response body type
  |> middleware.map_resp_body(bit_builder.from_bit_string)
  // Support PATCH, PUT, and DELETE from browsers
  |> middleware.method_override
}

Running a service

Once we have defined a service we want to run the service with a web server so that it can handle HTTP requests from the outside world. To do this we use a HTTP server adapter, such as one for the Elli Erlang web server.

import my_app
import gleam/http/elli

pub fn start() {
  elli.start(my_app.service, on_port: 4000)
}

Or one for the Cowboy Erlang web server.

import my_app
import gleam/http/cowboy

pub fn start() {
  cowboy.start(my_app.service, on_port: 4000)
}

Or even using the adapter for the Elixir’s Plug interface, so that a Gleam HTTP service can be mounted within an Elixir Phoenix web application.

defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller

  def show(conn, params) do
    conn
    |> GleamPlug.call_service(params, &:my_app.service/1)
  end
end

A list of all available server adapters can be found in the Gleam HTTP project’s README.

If you would like to see more examples of Gleam HTTP being used to create a web application see the echo server on GitHub. It includes all the above as well as other concepts such as routing and logging.

HTTP clients

HTTP services are only half the story! We also can use the HTTP library to make out own requests.

import gleam/http
import gleam/httpc

pub fn get_my_ip() {
  try response =
    http.default_req()
    |> http.set_method(http.Get)
    |> http.set_host("api.ipify.org")
    |> httpc.send
  Ok(response.body)
}

Here we’ve build a HTTP request and sent it using the httpc client adapter, returning the response body if the request is successful.

A list of all available client adapters can be found in the Gleam HTTP project’s README.

Try it out

If you want to try out the new version of Gleam head over to the installation page. I’d love to hear how you find it and get your feedback so Gleam can continue to improve.

Want to view some existing Gleam projects? Head on over to the awesome-gleam list. Looking for something to build in Gleam? Check out the suggestions tracker.

Supporting Gleam

If you would like to help make strongly typed programming on the Erlang virtual machine a production-ready reality please consider sponsoring Gleam via the GitHub Sponsors program.

This release would not have been possible without the support of all the people who have sponsored and contributed to it, so a huge thank you to them.

Lastly, a huge thank you to the contributors to and sponsors of Gleam since last release!

Thanks for reading! Have fun! 💜