Gleam Review AOC2024

Monday Dec 02, 2024

new pic, who's this?

- @gleamlang

Overview

This year for Advent Of Code I decided that I wanted to take Gleam for a spin. I've seen short videos on youtube of people demoing features of the language, and I thought it seemed interesting. On top of that I always wanted to get an erlang language under my belt, but I just couldn't get over some of the weird syntax of erlang. Promptly ignoring Elixir, I decided to go with the brand new shiny language, Gleam. Each section I will split into a "good", "bad", and "ugly" sub-section to discuss the language.

The language looks similar to Elixir, but minus some of the things that make me a little uncomfy. Like Elixir's visual similarity to Ruby, or it's use of atoms. Especially since atoms and modules are kinda-sorta the same thing (atoms can, but not always, reference a module). Now, I can't complain too much as an emacs user, since lisp uses atoms, but I find lisp to be more logically consistent in the definition of an atom.

Now, onto what I like about Gleam! Static typing is a big one for me. I generalyl have a strong dislike for languages that are not statically typed since, in order for you to be sure that your code will work, you have to test it with 100% coverage unless you have a linter that doesn't do just syntax errors. The compiler being able to tell you that something won't work is amazing. Another one of Gleam's selling points is that it is a full stack language, this is because it compiles to BEAM, and JS that runs on Deno, Node, Bun, and the web. I don't plan on using for web dev, but it's a neat feature of the compiler.

First Impressions (Day 1)

Good

I enjoy Gleam's pipe (|> operator. It is the exact same as the pipe operator in F#, as well as a handfull of other (mostly functional) languages. Some lisp dialects have some similar constructs. This helps to prevent curry-hell that you can find in haskell when you have to chain a bunch of functions together. It lets you read code in a normal left-to-right manner, rather than having to read backwards. You get the elegance of functional languages while somewhat maintaining readability.

It also has pretty decent compile times. Not that having only completed day 1 is enough to make a judgement. Even Rust compiles pretty fast when you stick to the standard library. All I'm saying is that it's faster than javac plus a Makefile, not to mention maven or gradle.🤢

Bad

After completing day one, what I have found lacking about gleam is the the standard library. In the version I am using (Gleam 1.6.2), the standard library seems to be lacking. The iterators in the standard library have been deprecated, and the compiler recommended that I use the package "gleam_yielder", which installed itself into the "gleam/" namespace, same as the rest of the standard library. The fact that the standard implementation of iterators is not part of the standard library is very odd to me. Plus, my C-brain doesn't like the fact that I have to reach out to a 3rd party package index when I want to start a new project. What if I don't have a network connection? I don't want to implement iterators by hand.

The standard library was also missing IO operations, that basicaly every other serious non-domain language provides. I had to import a 3rd-party package in order to read input from stdin. The same thing for command line arguments. I understand that this is probably because it is designed to be a full stack language, and obviously you can't read from stdin, or open a file (in the traditional sense) on the web. But I really don't like that those things are 3rd party. This is especially notiable since I updated my code to use gleam_yielders, but the stdin package returns an Iterator not a Yielder. Forcing me to live with the deprecation warnings.

Ugly

Gleam is a functional language that likes symbols. Now it's not Haskell levels of symbols, but there are quite a few. Or Rust when you need lifetimes.

There are no if, for, or while, statements! This was the most shocking thing to me. Instead Gleam opts for you to use the case statement for all control flow. Which I do think there is something neat to that, but it's very strange. I would also be curious as to how well the compiler optimizes that away. As far as loops go, the case statement solves that by allowing recursive cases. Which is actually a kind of neat idea. This feature also helps to solve curry-hell problem in Haskell.