I’m not exaggerating. After using Elm on and off for the last few years, I have encountered exactly one runtime exception.
Why is this? Elm’s type system, while lacking several abstractions
that make functional programming really fun, is quite good at
ensuring you don’t mess up. For example, when you case match on some
type, the compiler ensures that you cover all cases. Exceptions are
avoided in favor of using well-typed solutions like Maybe
throughout Elm libraries.
What was the One Exception then? It came about when I was writing a simple flood fill algorithm for my tiny pixel art app PixElm. My flood fill filled in nearby pixels by generating four recursive calls to the neighbors of a given pixel. For large numbers of pixels, too many calls were generated and the program ran out of stack, causing a crash.
fill : Color -> Point -> Model -> Model
fill colorToReplace pt model =
if getPixel model.pixels pt /= Just colorToReplace then
model
else
placePixel model pt
|> fill colorToReplace { pt | x = pt.x - 1 }
|> fill colorToReplace { pt | x = pt.x + 1 }
|> fill colorToReplace { pt | y = pt.y - 1 }
|> fill colorToReplace { pt | y = pt.y + 1 }
There it is. The only runtime exception I’ve encountered when writing Elm to date.
Note that this unfortunately doesn’t mean I found zero bugs in my
logic…and it also doesn’t mean Elm is perfect or truly has “no runtime
exceptions”. Luckily, I never use Debug.crash
and I guess I
miraculously avoided ever dividing by zero. There are some more obscure
ways to cause a crash, which I didn’t run into either. However, if your
goal is to avoid runtime exceptions as much as possible, Elm is probably
a pretty good place to be (short of not writing code at all).