The Gleam Tour is helpful.

import golfs

The order of top-level statements doesn't matter. Hence, if a top-level statement ends in }, it shouldn't be the last one (unless all do), otherwise you waste a byte:

import gleam/io pub fn main(){}
pub fn main(){}import gleam/io

If you're using a module enough times, it can save to use as:

int.a int.b int.c import gleam/int
i.a i.b i.c import gleam/int as i

If you are using one or more functions from a module enough times, use an unqualified import (the module itself is still imported):

l.map l.map import gleam/list as l
map map import gleam/list.{map}

l.map l.map l.min l.min import gleam/list as l
map map min min import gleam/list.{map,min}

as can also be used within the {}:

drop drop import gleam/list.{drop,map}
d d import gleam/list.{drop as d,map}

You can both use an unqualified import and alias the module name (the alias takes 4 bytes instead of 5 in this case):

l.drop l.min map map import gleam/list.{map}as l

Looping

range+map

Sometimes can be too long

list.map(list.range(1,100),fn(x){...})

fold

This method can be better than map under certain circumstances, be still needs the list import

list.fold(list.range(1,100),start,fn(acc,x){...;new_x})

fn

A recursive function would work well in many scenarios because you can just avoid the list import altogether, this is usually the best way to loop

pub fn main(){f(100,100)}
fn f(a,b){io.println(...)cond&&f(a*2,b/2)}

You can also use || instead of && if !cond ends up shorter than cond.

case

Generally it is better to avoid case, but if you have to, and you know the possible values of your condition:

case a%20!=2{True->a _->0}
case a%20{2->0 _->a}

Printing

Newlines within string literals are allowed, hence it might be possible to save a byte by using print instead of println and writing the newline out manually.

Sometimes you have to print a value if a condition cond is satisfied, and, in such a case, you might be inclined to use case; however, remember that print and println return Nil, and Nil==Nil, hence you can do one of these:

cond&&Nil!=io.println(str)
!cond||Nil==io.println(str)

Nil should be on the left side of the ==, to save a space. The first version is always False, and the second one is always True (also consider if !cond is shorter than cond). If you want the expression's value to depend on cond, change between == and !=.

Unwrapping Results

Result types can be useful for production code, but they're near useless for code golf, since they almost always end up as successes. gleam_stdlib provides unwrap in gleam/result, which returns the content of the Result if it's Ok(...), and a default value if it's Error(...). For instance, unwrap(int.parse(str),0) parses str to an Int, and returns 0 if str doesn't have a valid integer.

However, to use it, you unfortunately have to include import gleam/result in your code. Luckily, there is a shorter alternative, to just do the job manually, if you don't somehow need another function from gleam/result (you might have to change the 0 to a value of the correct type):

result.unwrap(r,0)import gleam/result
case r{Ok(x)->x _->0}

If you'd have to use unwrap multiple times, making a new function is still cheaper:

u(q,0)u(r,0)import gleam/result.{unwrap as u}
u(q)u(r)fn u(x){case x{Ok(x)->x _->0}}

If you have to handle different types, then you'll have to provide a default value, but literally reinventing unwrap still saves:

u(q,0)u(r,"")import gleam/result.{unwrap as u}
u(q,0)u(r,"")fn u(x,y){case x{Ok(x)->x _->y}}

ASCII value

To get the ASCII value of a character

let assert[a]=string.to_utf_codepoints("h")string.utf_codepoint_to_int(a)

Misc tips