Elm is a statically typed language that allows us to make impossible states impossible. But for those newer to Elm it isn't always obvious how to use this super power to solve their practical problems.
If you are still feeling a bit unsure yourself then join us as we use types to tackle a common feature request of web apps: adding validation to forms.
We'll start with a limited example taken from the Elm guide and iteratively work our way towards a solution with custom types. After attending this talk you'll have a solid mental model to apply not only to form validations but to any code in your application.
This talk starts with a simple example and iteratively improves upon it. In each step we move implicit assumptions about our data and its state into more explicit data structures. Finally, we end up with several custom types that allow the compiler to guide us as we extend our forms in the future.
This talk will be useful to the Elm community because form validation is one of the most common questions that come up in the Elm slack and forums. I will be able to answer a common question with practical how-to, while also having a great use case to explore the power of static types to folks who may still be coming to grips with them.
I am well suited to give this talk for two reasons. First, I've only been writing Elm for about a year, and still have a lot of beginner perspective. Second, I've worked for the last 9 months on an Elm SPA built entirely around handling complex form data. Many of the final examples here are inspired by working in that code base every day.
Below is a rough outline of the evolution we'll walk through:
- Only store the raw
String
value of the input in the Model. - Validate the raw value at the view layer to conditionally display an error.
- It works, but we have to remember to put the raw
String
through the validating function(s) whenever we want to do something with it. - There's a large surface area for bugs to creep in, and the compiler can't help us very much to catch them.
- In addition to storing the raw
String
input values, we'll add aDict
to store error state. We'll use field name's as keys and error strings as values. - Now we have our validation state in the model at least.
- There is a potential for impossible states - misspelled keys, forgetting to clear an error from the Dict. We can improve that a bit with a simple Custom Type for field names, but its still not great.
- We also have an implicit assumption of a 1 to 1 relationship between fields and errors.
- We know when a field has an error, or how to look for it, but the compiler still has no idea if a field input is valid or not.
- Store each field as a tuple of:
( String, Maybe String )
- Now we are explicit that we always have a raw string value and only 0 or 1 errors to show.
- We've surfaced a hidden Maybe (
Dict.get
) which is nice.
- Now we have:
( String, Result String a )
- Now we can parse the raw String value into whatever we want (an Int for age, for example), and know its valid at the type level.
- More of our assumptions are captured and reflected in the data structure.
- The impossible states are in fact impossible now.
- Introduce a
FormField
custom type that captures all of the possible states and makes impossible states impossible. - We can represent more states than with
Result
, such as anInitial
for a field that hasn't taken input yet (we don't want to show an error until it has been interacted with). We might also want to addUnvalidated
, that would let us store the dataonInput
and validateonBlur
for a better user experience.
- Change the name to
Validatable
. Mention theericgj/elm-validation
library that we basically implemented. - With our custom type, we can define a
andMap
andmapN
function that will allow us to combine multipleValidatable
values into a singleValidatable
when we need to know whether the form as a whole is valid and ready to be submitted.
My plan is to assemble a fair number of slides to walk through showing code snippets and visualizations of the data structures/transformations we've made. My goal is to have each step small enough as we go that the code won't overwhelm. I will also have links to several Ellie Apps for those who want to jump in. I was planning on sharing links to them in the chat for the video conference or otherwise before I get started.