The Syntax Cliff
Teaching syntax with Elm 0.19.1
by Evan Czaplicki / 21 Oct 2019

When you start learning a programming language, how much time do you spend stuck on syntax errors? Hours? Days? As someone who once spent a full day trying to debug a MapReduce build before learning that you need backslashes in multi-line bash expressions, I know it is a deeply discouraging amount of time. Lots of people have suffered through experiences like this with missing semi-colons and curly braces, but how many people do not make it past these syntax errors? How many people fall off the syntax cliff and give up on a language or just quit programming entirely?

tightrope

I started wondering how much of this problem comes down to error message quality. Could I get the compiler to a point where people feel like it is actually helping them learn Elm syntax?

So with the release of Elm 0.19.1 today, I am excited to share the new and improved syntax error messages! My hope is that the new compiler feels more like a teacher, showing helpful and relevant examples when you get stuck. The remainder of this post highlights some of the messages that people are likely to see when learning Elm, so you can decide for yourself!

Note: This is a patch release, so existing users should be able to just download the new version and start using it with existing projects.

Learning New Syntax

Say you are learning Elm and do not know how to import modules yet. Maybe you just try the JavaScript way to see if it works:

import * as Set from 'set' type alias Student = { firstName : String , lastName : String , completedAssignmentIds : Set Int } toFullName : Student -> String toFullName student = student.firstName ++ " " ++ student.lastName
-- EXPECTING IMPORT NAME ------------------------------------------ src/Main.elm I was parsing an `import` until I got stuck here: 1| import * as Set from 'set' ^ I was expecting to see a module name next, like in these examples: import Dict import Maybe import Html.Attributes as A import Json.Decode exposing (..) Notice that the module names all start with capital letters. That is required! Read <https://elm-lang.org/0.19.1/imports> to learn more.

The new error message points to the spot where it got stuck, but more importantly, it tries to help by (1) giving examples and (2) linking to a page that explains how imports work. It tries to help you learn!

This syntax error is pretty easy to detect though. What about harder cases?

Missing Curly Braces

Have you ever forgotten a curly brace in JavaScript? Forget one little character and the error message shows up at the very end of the file. I still struggle when I get that message even after a decade of experience with JavaScript!

One of the major goals with the new parser was to improve this particular type of error message. Say you are defining a Student but forget the closing curly brace:

import Set exposing (..) type alias Student = { firstName : String , lastName : String , completedAssignmentIds : Set Int toFullName : Student -> String toFullName student = student.firstName ++ " " ++ student.lastName
-- UNFINISHED RECORD TYPE ----------------------------------------- src/Main.elm I was partway through parsing a record type, but I got stuck here: 4| { firstName : String 5| , lastName : String 6| , completedAssignmentIds : Set Int ^ I was expecting to see a closing curly brace next. Try putting a } next and see if that helps? Note: I may be confused by indentation. For example, if you are trying to define a record type across multiple lines, I recommend using this format: { name : String , age : Int , height : Float } Notice that each line starts with some indentation. Usually two or four spaces. This is the stylistic convention in the Elm ecosystem.

The error message actually suggests a viable fix!

Elm has a rule that any definition must be defined on a fresh line. It cannot have any spaces in front of it. One benefit of this rule is that the compiler can always pinpoint the particular definition that contains a syntax error. No more errors at the end of the file!

Another example of using rules to get guarantees appears with variable naming.

Variable Naming

Elm uses capitalization to differentiate certain kinds of names. Module names start with an upper-case letter, like List and Http, whereas function and argument names start with a lower-case letter, like length and request. These rules make it easier to scan through new code, but they can be surprising in the learning phase, especially if you are used to JavaScript or English.

So when someone is playing with the examples in the online editor, making a capitalization mistake will go something like this:

import Set exposing (..) type alias Student = { firstName : String , lastName : String , completedAssignmentIds : Set Int } ToFullName : Student -> String ToFullName student = student.firstName ++ " " ++ student.lastName
-- UNEXPECTED CAPITAL LETTER -------------------------------------- src/Main.elm Declarations always start with a lower-case letter, so I am getting stuck here: 3| ToFullName : Student -> String ^ Try a name like toFullName instead? Note: Here are a couple valid declarations for reference: greet : String -> String greet name = "Hello " ++ name ++ "!" type User = Anonymous | LoggedIn String Notice that they always start with a lower-case letter. Capitalization matters!

The new error message (1) suggests a fix and (2) gives examples to help teach the overall rules.

I encourage you to try to make naming errors in a language you are already familiar with. Can you imagine making that mistake when you started with the language? How is the error message? Would you know what it meant if you were just learning to program?

Survivorship Bias

Trying to improve error messages seems like a worthwhile idea, so why is it uncommon for compilers to have syntax error messages like this? And why did it take so long for Elm to prioritize this project? I think part of the answer is survivorship bias.

Syntax errors are highly concentrated in the first weeks with a language, and people are particularly vulnerable in this time. When a beginner asks themselves why something is hard, it is easy to think, "Because I am bad at it!" And it is easy to spiral from there. "I heard it was hard. I was not super confident I could do it anyway. Maybe I just suck at this. And if this is what programming feels like, there is no chance I want to be doing this with my life!" People who fall off the cliff cannot share their perspective in meetups, online forums, conferences, etc. They quit! They are not in those places!

As for people who make it past the cliff, many do not shake off that initial confidence blow. They use the language, but not with enough confidence to think that their problems should be handled by a language designer. "Oh, that again. I will never learn!"

So language designers never really hear about this problem. I only understood its magnitude once elm/error-message-catalog got going. That repo solicits confusing error messages in hopes of finding ways to improve. I think projects like that legitimize the idea that "error messages should be better" such that I started hearing from a broader range of people. (Not just the very non-random sample of users that participate online!)

I personally think survivorship bias is a huge trap for language designers when it comes to prioritization. "Everyone is telling me to work on something else!" I find it really hard to put that aside even when I know "everyone" is actually a very particular sample, and I imagine it is only harder for language designers at the big firms with bosses prioritizing adoption by existing programmers over everything else. "Is this language bringing developers to our cloud services? Is this VM bringing people to the default search provider in our browser? Is the reputation of this project making talent acquisition cheaper?" People who work on programming languages understand the implicit conditions of their employment, and it varies a lot by project.

Point is, I hope this work on syntax error messages will help make Elm more friendly and accessible, and I hope it will help make space for other language designers to prioritize this kind of project!

Try it out

If you are interested in exploring the new syntax error messages with Elm 0.19.1, you can experiment with examples in our online editor or start working through the The Official Guide.

I am sure there are ways the messages can be improved further, so I encourage you to share any confusing error messages in elm/error-message-catalog if you run into a tough one while learning!

I appreciate folks taking the time to give Elm a shot, and I hope it is fun to explore!

Thank You

A lot of folks helped out testing Elm 0.19.1, but I want to specifically thank @jfmengels for stress testing the syntax error messages and @hrk for reading the 5800+ line syntax error file straight through! Both found a bunch of ways to improve the error messages even more.

I also want to thank the folks who have reported confusing error messages to elm/error-message-catalog over the years. It has been extremely helpful in inspiring and guiding projects to improve error message quality.

Thank you!