Does Elm fill in your business logic and build your app for you, or fix bugs in your code's logic? If not, then how can it be that "if it compiles, it works?"
Let's set a bounding box around this statement as a starting point. I think we can agree that on the one extreme, Elm does not fix your bugs or write your business logic for you. So if it means anything, the phrase "if it compiles, it works" at least has a limit to its scope.
On the other extreme, when our Elm code is compiling there are some basic things that we expect (at least in practice):
So "if it compiles, it works" is somewhere between meaningless and true without caveats.
People often talk about the experience of safe refactoring in Elm. You can do a sweeping change and feel confident that you didn't break anything once you get it compiling again.
Even though Elm doesn't write our business logic for us, it does have a lot of tools to remind us of cases to consider. When you add new functionality, it's a common experience to have the compiler walk you through several places that now have an inexhaustive case expression. You can bypass this, so it doesn't come for free. It's our job to use the tools that Elm gives us so the compiler can safely guide us through adding new functionality (see [When It Compiles, But Doesn't Work]).
Opaque Types also help us feel confident about our wiring beyond just having the right type of data. It can help give meaning to this data, and to give us guarantees about things like the origin of the data and how/where the data is used. See [Using elm types to prevent logging social security #'s] and [Entry Gatekeepers].
Can working from a clean slate give us the feeling that if it compiles it works? If Elm isn't writing our code for us, then it can't get us all the way there. But I have often experienced the feeling of writing an Elm custom type, being sure to wire it up (through
view, etc. - of course it won't work if I forget that), and then once it's wired in it does seem to "just work."
My personal experience with this is that it applies to wiring, which in other languages is often something that I have to take a lot of care to get right and spend a lot of time debugging as I build features from scratch. When it comes to building up sophisticated business logic, I start by writing unit tests and iterate on that, so that's far from "if it compiles, it works." It's more of an iterative process where it goes through several stages of completion where it's compiling but doesn't handle all of the cases (it only does enough to make my test cases pass).
So for me, the phrase "if it compiles, it works" doesn't apply to writing my business logic from scratch, but it does apply to the wiring and that in turn lets me focus on my business logic more.
If you use tools to help you wire external systems safely to your Elm code ([Types Without Borders]), then you can extend this confidence about correct wiring to the parts of your code that communicate with external systems.
When I'm writing my unit tests in Elm, I also feel more confident that "if my tests pass, it works." Why would this be different in Elm than working in other languages? One major factor is that Elm doesn't need mocks (nor does it have the ability to do mocks since it's not a dynamic language). You can use dependency injection. In fact, this is baked into the paradigm with things like random numbers, the current date, or other non-deterministic data: the only way to get it is to receive it in
update from the Elm runtime.
Because I know my Elm code doesn't depend on global environment, implicit side effects, and non-deterministic functions, I can trust the green I get when my unit tests pass. Testing that an input gives an output removes many testing pitfalls and painpoints. The Elm language makes testing easy and safe, so that's all the more reason to test your Elm code.
That's also a good reminder that type-safety is a complement to automated tests, not a replacement for it. I can say for certain that my confidence changing my Elm applications would be very low if I didn't have automated tests.
What does "If it compiles, it works" mean to you? And what are the limits? When does it compile, but not work? I'd love to hear your experiences.
Sign up to get my latest Elm posts and course notifications in your inbox.
Pure Elm content. Unsubscribe any time.
Types Without Borders
How does the bartender at your local bar know it's okay to serve you? In many cases, it's because the bouncer already checked your ID before you entered. This...
Using elm types to prevent logging social security #'s
One of the most successful techniques I've seen for making sure you don't break elm code the next time you touch it is a technique I call an Exit Gatekeeper.
When It Compiles, But Doesn't Work
In Elm code, "if it compiles, it works." Does that come for free? Let's see if we can find Elm code where "it compiles, but it doesn't work" to see what we can...