Gleam notes

11 December, 2020

Meta

This document should be accessible at https://jmnorlund.net/posts/2020-12-11-gleam.

Introduction

Gleam is a language which runs on Erlang/OTP, it is is statically typed and compiled to Erlang.

Running code

use rebar3 shell, if you want to recompile from within the shell you can use r3:do(compile).

Adding and running new files

Say you add a file to src/app called foo.gleam which contains a function bar, you can reference it in the shell via app@foo:bar().

Running tests

rebar3 eunit

Syntax

Functions

Labelled arguments

pub fn replace(
  in string: String,
  each pattern: String,
  with replacement: String,
) {
  // The variables `string`, `pattern`, and `replacement` are in scope here
}
replace(in: "A,B,C", each: ",", with: " ")

Anonymous functions

pub fn run() {
  let add = fn(x, y) { x + y }

  add(1, 2)
}

Function capturing

pub fn add(x: Int , y: Int ) -> Int {
  x + y
}

pub fn run() {
  // This is the same as add(add(add(1, 3), 6), 9)
  1
  |> add(_, 3)
  |> add(_, 6)
  |> add(_, 9)
}

Pipe operator

pub fn add_one(x) {
  x + 1
}

pub fn three() {
  1
  |> add_one
  |> add_one // 3
}

Data Structures

pub type Person {
  Person(
    name: String,
    gender: Option(String),
    shoe_size: Int,
    age: Int,
    is_happy: Bool,
  )
}

pub fn have_birthday(person) {
  // It's this person's birthday, so increment their age and
  // make them happy
  Person(..person, age: person.age + 1, is_happy: true)
}

Types

Constants

const

const hello: String = "Hello"

pub const start_year = 2101
pub const end_year = 2111

pub describe(year: Int) -> String {
  case year {
    year if year < start_year -> "Before"
    year if year > end_year -> "After"
    _ -> "During"
  }
}

Strings

There is no string concatenation operator in Gleam, instead you use the function string.concat which takes a list of strings and returns a concatenated string.

import gleam/string

string.concat(["foo","bar"]) // "foobar"

Conditionals

There is no if/else in Gleam, the only flow control mechanism in Gleam is case expressions.

Case expressions and guards

Code examples

How would you write this piece of Haskell if you were to write it in Gleam?

Foo n
  | n == 0 = doThis
  | n == 1 = doThat
  | n == 2 = doAnother
  | (n >= 3 ) && (n <= 99) = doDefault

NobbZ Idag 09:35 Untested, as I currently don't do active gleam:

case n {
  0 -> doThis
  1 -> doThat
  2 -> doAnother
  n if 3 <= n && n <= 99 -> doDefault
}

Guards & limitations of guards in Gleam, compared to Haskell

lpil: one difference between guards in Gleam and in Haskell is that in Gleam only a subset of expressions are permitted there, while Haskell permits any expression. The Gleam ones run in constant time and get optimised by the Erlang compiler. It’s a virtual machine limitation

jmn Idag 10:25 so I can't do:

pub fn test_one(x) { x > 100 }
pub fn test_two(x) { x < 100 }

pub fn guard_function_case(n) -> String {
  case n {
    n if test_one(n) -> "Larger than hundred."
    n if test_two(n) -> "Smaller than hundred."
    _ -> "One hundred"
  }
}

or can I?

lpil Idag 10:26 Functions cannot be called in guards as they might diverge Though those function bodies could be used instead As the int comparison operators are permitted

Example code

import gleam/string

pub fn hello_world() -> String {
  "Hello, from gthree!"
}

pub fn hi(hi: String) -> String{
  string.concat(["hi, ", hi])
}

Type checking example:

pub fn moo() -> String {
  hi(1)
}

results in:

error: Type mismatch
   ┌─ /home/jmn/git/gthree/src/gthree.gleam:12:6
   │
12 │   hi(1)
   │      ^

Expected type:

    String

Found type:

    Int