isaac zone

curious explorer

Opening and editing files with Gleam

27th of May, 2025

Gleam is a wonderful functional programming language that compiles to both Erlang and JavaScript. One of the interesting things about Gleam is how small the standard library is. To remain completely platform independent, the Gleam stdlib doesn't provide any file I/O. With that being said, JavaScript backend runtimes and the Erlang BEAM all support file I/O, and for that, we use the incredible Simplifile package.

Simplifile by Benjamin Peinhardt (bcpeinhardt on GitHub) is a high quality package that supports Erlang, and JavaScript compilation targets, but of course won't work in the browser which has no file I/O capability. Simplifile handles file I/O purely synchronously, which is something you may want to consider if you're working with incredibly large files. With that being said, I can't say there would be many situations where this would matter a whole lot. If you really do need more performance, you might need to look into writing some FFI. You really shouldn't reach for FFI before using a package, though.

For transparency's sake, I wrote this post in the hopes of getting better SEO to simplifile so people like me stop thinking the defunct gleam_file package is still the way to handle files. If you would rather just read through documentation than my waffling on in this blog post, Simplifile is well documented on Hex. I also use Gleam (and Simplifile) to generate this blog, so I can vouch for its effectiveness!

Reading text files with Gleam and Simplifile

Simplifile has a straight forward function for reading files, simply called read, taking a path to the file and returning a Result type containing the string (so, Result(String, simplifile.FileError)). Gleam results are great!

import gleam/io
import simplifile

pub fn main() {
  let assert Ok(contents) = simplifile.read(from: "file.txt")

  // If you've done everything right, this should print the text from the file "file.txt"!
  io.println(contents)
}

Pretty cool! We can do some useful stuff by reading files.

Reading binary files

import gleam/bit_array
import gleam/int
import gleam/io
import simplifile

pub fn main() {
  case simplifile.read_bits(from: "file.png") {
    Ok(contents) -> {
      bit_array.byte_size(contents)
      |> int.to_string
      |> io.print

      Nil
    }
    Error(e) -> {
      // Uh oh! Something went wrong opening that file.
	  // Let's debug print it, since this isn't a real app.
      echo e

      Nil
    }
  }
}

There's also, of course, functions to read directories, to write files, and all your standard copy and move functions. I've got a little demonstration of all that in action here. Just keep in mind that this isn't valid Gleam you could post right into a file, because it's missing imports and function declarations. You also shouldn't be leaning on let assert too much, because it'll panic and kill your app.

// Recursively create the dist directory structure we want
simplifile.create_directory_all("./dist/pixels")

// Read a directory
let assert Ok(entries) = simplifile.read_directory("./pixelart")

// Loop through it and copy all the entries over to a build directory
list.each(entries, fn(entry) {
  simplifile.copy("./pixelart/" <> entry, "./dist/pixels/")
})

That's all for the examples! If you want to see what else Simplifile can do, check the docs. It's a great library, and as I mentioned earlier I use it for this blog. The posts are written in djot, read by Simplifile, and printed out as HTML inside a template. It really made me realise how non-magical static site generation is. I love it!

It's been a bit of a trip for me, as someone who uses a lot of JavaScript and Go, to use a language with such a small standard library. I think it's a really good approach for a language like Gleam though, and decoupling things actually makes updating and refactoring stuff really easy. Gleam is overall a super pleasant language to use, especially for web-based stuff. I'm looking forward to sharing more of what I do with it here.

that's all from this post. thanks for reading!