Adventures in Elm
Elm is Made of Haskell
Elm takes a rather blatant preference for functional programming:
Why a functional language?
Forget what you have heard about functional programming. Fancy words, weird ideas, bad tooling. Barf. Elm is about:
- No runtime errors in practice. No null. No undefined is not a function.
- Friendly error messages that help you add features more quickly.
- Well-architected code that stays well-architected as your app grows.
- Automatically enforced semantic versioning for all Elm packages.
Redux, I am Your Father
Let’s explore what it’s like to work with Elm. How do we handle errors? How do we debug? Is it as beginner-friendly as it claims?
First, I need to get everything installed. After running the Mac installer, I have all the Elm goodies on my
elm-package. That took all of 3 minutes. No hiccups so far. Following the Elm instructions, I next installed the “elm” extension for VS Code. After installing, I can create an Elm file and get nice syntax highlighting, as shown in their example gif:
I also installed the
elm-format extension. So far so good! The remainder of the docs cover the Elm language and Elm architecture. I read through these fairly brief overviews, but I won’t rehash them here. Let’s try to get a simple app running.
On the command line:
Per the docs, elm-reactor is a way to “get a project going quickly.” We should be able to create a Main.elm file, put some code in it, and run elm-reactor in order to build and run the file in the browser. Let’s try that. I opened the “hello-elm” directory we just created in VS Code, and I see an “elm-stuff” directory and an “elm-package.json” file. Now I’ll create a file called “Main.elm” (following Elm file name conventions, the entry module is called “Main” and all modules are capitalized). I’ve added these lines in the file as an initial test:
Now to run it via elm-reactor. On the command line in our project directory:
Now we just go to localhost:8000 and click the “Main.elm” link to build and run our module.
Success! So far there’s been absolutely no fuss following the setup and first app instructions.
Using the Elm architecture
Now let’s try the canonical counter example given in the docs. Replace the contents of Main.elm with the following:
We now have a working example of the Elm architecture.
So far I’ve been impressed with the out of the box features and simplicity of Elm’s tooling, but how does Elm handle compiler errors? Can we sneak anything past the compiler? Let’s give that a shot. I’ll make an intentional mistake in Main.elm and try to use something that is undefined:
First off, notice that the VS Code Elm extension catches this. Awesome! Now let’s see what elm-reactor has to say about it:
Not only is Elm refusing to compile this, it gives us some suggestions for a possible fix. The error messaging overall is pretty useful. This is a much needed departure from the cryptically academic error messages of the older functional languages which inspired Elm.
I tried this:
Well, the compiler foiled us at every turn, but one way to crash Elm at runtime is to do so on purpose with the tool they created for this job!
The Elm compiler attempts to remove logical errors in your code, but the app is still going to run on the web (“the most hostile software engineering environment imaginable”). Errors are going to happen from timing failures, incompatible browsers, and invasive plugins. We’re not going to get very far without monitoring and debugging. So how does Elm’s debugger work? Can I trace variables? Can I set breakpoints and inspect? Much to my surprise and disappointment, the official guide has no information about debugging!
However, I did notice when running Elm files via reactor that there’s a built in debugger attached.
This feels very similar to Redux devtools (reverse and replay actions, see the app state after any given action, etc), but, despite its benefits, this tool doesn’t meet my expectations for overall debuggability.
I found very few trustworthy and up to date resources on Elm debugging. I tried piecing together what scant information I could dig up via google, but all the sources seemed to conflict. For example, most of the debugger functions don’t seem to be present in the version of Elm I have installed (0.18.0, the latest official release at the time of writing). Luckily, VS Code saved me.
So it appears that
Debug only has two methods,
log, one of which we’ve already used. Let’s try the other one. Gleaning what I can from the method signature, I tried using
log like this:
I’m inferring that
Debug.log takes one string argument to use in the log output, one argument of any type of value, and then returns the second argument so that the debug expression evaluates to that argument (is replaced by it) making it a no-op in relation to the surrounding code. Let’s see if elm-reactor agrees with me:
It works! Cool.
I’ve been able to fairly easily figure out how to use the native
Debug module in Elm thanks VS Code’s Elm integration. However, the lack of documentation and apparent instability of the approach to debugging in Elm is the only pain point I’ve come across so far.
Under the covers, the debug module will expose messages through the
console object, where TrackJS will automatically capture them as part of the Telemetry Timeline. Integrating TrackJS monitoring into your Elm app is pretty straightforward. First we need to make an index.html with
<script> generated by Elm
Tradeoffs of Compilation
Debugging is also a desirable tool for development in Elm when our code compiles, but does not behave as expected. For example, bottomless recursion will not cause a compiler error: