Skill Issue logo

Skill Issue

Subscribe
Archives
January 17, 2025

Other Languages

I got into programming to make games. Having heard that the game industry was a soul-crushing place, I kept it as a hobby. At some point, I abandoned it altogether. Now I'm back.

I spent the holidays working on TownWizard.net, my roguelike-inspired website. It's built in ReScript and React with a makeshift functional ECS under the hood. It may or may not be in a playable state when you read this.

Working on the project made me realize how much I missed making things for fun. My conversation with Noah Gibbs in November got me thinking about how I spend my time, and especially my programming time. Sadly, Noah passed away not long after we had that conversation.

In Noah's honour, I'm going to make sure I have more fun programming this year. Here's some code I found fun to write:

->Belt.SortArray.stableSortBy(
  ((p1, _), (p2, _)) => {
    switch (p1, p2) {
    | (Some(Presence.Impassable), Some(Presence.Impassable)) => 0
    | (Some(Presence.Large), Some(Presence.Large)) => 0
    | (Some(Presence.Small), Some(Presence.Small)) => 0
    | (Some(Presence.Nothing), Some(Presence.Nothing)) => 0
    | (Some(Presence.Impassable), _) => -1
    | (_, Some(Presence.Impassable)) => 1
    | (Some(Presence.Large), _) => -1
    | (_, Some(Presence.Large)) => 1
    | (Some(Presence.Small), _) => -1
    | (_, Some(Presence.Small)) => 1
    | (Some(Presence.Nothing), _) => -1
    | (_, Some(Presence.Nothing)) => 1
    | _ => 0
    }
  },
)

The code accepts an array via the pipe operator (->). The array contains tuples where the first value is of type option. Presence.t comes in four flavours: Impassable, Large, Small, and Nothing. These "flavours" are called "variants", but that's beyond the scope of this issue. Pretend that the Presence.t type is basically just an enum with four options.

The goal of this code is to sort the items by their "passability". The pattern matching rules define the relative ordering. We start with four rules that define that identical presences are equivalent:

| (Some(Presence.Impassable), Some(Presence.Impassable)) => 0
| (Some(Presence.Large), Some(Presence.Large)) => 0
| (Some(Presence.Small), Some(Presence.Small)) => 0
| (Some(Presence.Nothing), Some(Presence.Nothing)) => 0

We then define rules that define Impassable as greater than anything else. _ is a placeholder that matches any value. We know that the _ can't be Impassable, because that would be covered by an earlier case.

| (Some(Presence.Impassable), _) => -1
| (_, Some(Presence.Impassable)) => 1

If we haven't matched one of these cases yet, then we work down, defining rules for progressively less "passable" presences.

| (Some(Presence.Large), _) => -1
| (_, Some(Presence.Large)) => 1
| (Some(Presence.Small), _) => -1
| (_, Some(Presence.Small)) => 1
| (Some(Presence.Nothing), _) => -1
| (_, Some(Presence.Nothing)) => 1

Switch statements in ReScript (and other ML-style languages) have to be exhaustive. Either of the variables (uh, "bindings") we're comparing could be None, rather than Some(_). In all the patterns so far, we gracefully handle the case where one side is None with the _, but we've still left out one case: the case where both items we're comparing are None. We handle this with a base case.

| (None, None) => 0

If we add new variants of Presence.t, the compiler will detect that this switch statement is no longer exhaustive, and make sure we handle those cases. Thanks, compiler!

Pattern matching in ReScript is very powerful, but it has limits. For example, you can't put computed values in your patterns, because the compiler can't know at compile time if the switch will be exhaustive.

ReScript (and OCaml, and other ML-style languages) offer a lot of productivity. I may be relatively new to these languages, but the sound type system, editor tooling, and compiler messages point me quickly to the mistakes I've made.

I've got a little more ReScript to write for this project, but after that I'm going to start learning Nim. Why Nim? That's a surprise for a later Skill Issue.

In the meantime, go listen to Παραμαινομένη by Ὁπλίτης. Ὁπλίτης is Greek for "hoplite", but don't let that fool you into thinking this artist is Greek; he's actually Chinese.

Don't miss what's next. Subscribe to Skill Issue:
GitHub Bluesky X LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.