axelerator.de

Speed coding Minesweeper in a statically typed functional language

5 minutes
#elm
19 September 2024

Elm can be just as agile for prototyping as dynamic progamming languages! I challenged myself to created and deploy a browser-based Minesweeper clone in under 2.5 hours and less than 100 lines of code.


The Challenge: Statically Typed Prototyping

screenshot of Radan Skorić's personal site Read the full article “Exercise: Minesweeper in 100 lines of clean Ruby” on Radan Skorić’s personal site .

Recently, I stumbled upon a blog post titled “Exercise: Minesweeper in 100 lines of clean Ruby”.

As a developer who’s been working with Ruby since 2006, I’ve long appreciated its readable syntax and extensive ecosystem. The ability to rapidly prototype in Ruby has been a cornerstone of my career, serving both startups and multi-million user applications. But I also had my fair share of grief with specifically Ruby on Rails. The default to mutability and the lack of proper static analysis often led to the creation of systems that are hard to maintain and extend.

Reading the article sparked an idea. Could I replicate this feat in Elm, a statically typed, strict functional language often perceived as slower for prototyping?

I set myself a challenge:

  1. Recreate Minesweeper in Elm
  2. Keep it under 100 lines of code
  3. Complete it in less than 2.5 hours
  4. Include deployment

And to raise the stakes, I decided to stream the entire process on Twitch.

I also uploaded the recording to YouTube with detailed chapter jump points in the description.

Breaking Down Preconceptions

Before we dive into the results, let’s address the elephant in the room. Functional and statically typed languages often face prejudice:

  • They’re seen as slow to work with
  • Prototyping is believed to be cumbersome
  • The need to define everything upfront is viewed as a hindrance

These assumptions couldn’t be further from the truth, and I was about to prove it.

The Elm Advantage

From the Elm guide:
If a value can ever be used in an invalid way, the compiler tells you about it with a friendly error message. This is called type inference. The compiler figures out what type of values flow in and out of all your functions.

One of Elm’s standout features is its type inference. This allowed me to work quickly without writing explicit type signatures, addressing a major concern of dynamic language proponents. Throughout the session, the compiler provided helpful error messages based on inferred types, guiding my development process.

The Development Process

Setting up the Elm app from scratch and configuring contious deployment with GitHub Actions took less than 15 minutes. From there, I progressively extended the example app from the official Elm guide, transforming it into a fully functional Minesweeper clone.

A key strength of Elm became apparent early on: the browser as the default target platform. Unlike the Ruby version which relied on command-line input, our Elm Minesweeper offered a clickable UI, making it significantly easier to verify gameplay.

I’m a big fan of Elm’s expressive power but I don’t want to go into implementation details too much. So here is a little snippet that demonstrates one of the many ways that contribute to writing readable code.

This code calculates the number of surrounding bombs. Notice how the |> operator allows us to describe pipeline-like operations clearly, a hallmark of functional programming. The |> operator basically passes the output of a line as last argument to the function call on the following line.

numBombs =
  [(-1, -1), (0, -1), (1, -1) , (-1, 0),(1, 0) , (-1, 1), (0, 1), (1, 1)]
  |> List.map (\(x,y) -> (tx + x, ty + y))
  |> List.filter (inBounds width height)
  |> List.filter isBomb
  |> List.length

Challenges and Solutions

The biggest hurdle came about two hours in when implementing the recursion for revealing empty fields. This is usually solved recursively so of course I creted an infinite loop. The missing criteria was not to recurse on fields that have already been revealed. Elm is very expressive and as a result the whole function to respond to a click a field fits in 20 lines. So it just took some proper staring at the code and mulling over to find the missing condition.

Reflections and Implications

I was surprised by how much the Elm compiler assisted me, even without custom types and annotations. This experience reinforced my belief that statically typed languages like Elm are not just viable for rapid prototyping – they excel at it.

The challenge of staying under 100 lines forced me to omit function signatures entirely. Yet, Elm’s type inference still provided robust error checking and guidance. This challenges the notion that statically typed languages require extensive upfront type definitions.

Conclusion

This experiment proves that statically typed languages like Elm can be just as agile for prototyping as their dynamic counterparts. The ability to create and deploy a browser-based game in under 2.5 hours, with the added benefits of type safety, is a compelling argument for exploring alternatives to traditional scripting languages.

Whether you’re a comfort-zone developer or someone caught in the whirlwind of ever-changing JavaScript frameworks, I encourage you to give Elm a try. You might be surprised at how quickly you can turn ideas into working, deployed applications.

Remember, the goal isn’t to replace your favorite tools, but to expand your toolkit. Elm offers a unique blend of speed, safety, and simplicity that might just change the way you approach your next project.

What are your thoughts? Have you had similar experiences with statically typed languages? I’d love to hear your perspective in the comments below!

Resources for Your Elm Journey

If this has piqued your interest in Elm, here are some resources to get you started: