Skip to content

Instantly share code, notes, and snippets.

@gnfisher
Last active June 1, 2020 22:36
Show Gist options
  • Save gnfisher/f1c0d82672369b58fe465e019d2dda62 to your computer and use it in GitHub Desktop.
Save gnfisher/f1c0d82672369b58fe465e019d2dda62 to your computer and use it in GitHub Desktop.
Forms, Validations, and Static Types

Forms, Validations, and Static Types

Pitch your talk to the audience

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.

Pitch your talk to the organizers

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:

Validate the raw input at the view layer for display (Elm Guide example)

  • 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.

Add a Dict of errors keyed by field names

  • In addition to storing the raw String input values, we'll add a Dict 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.

Get rid of the Dict, use a Tuple instead of a String

  • 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.

Swap Maybe for Result, store the parsed value of the input

  • 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.

Custom types replace Result to allow more nuance

  • 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 an Initial 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 add Unvalidated, that would let us store the data onInput and validate onBlur for a better user experience.

Using mapN to validate an entire form

  • Change the name to Validatable. Mention the ericgj/elm-validation library that we basically implemented.
  • With our custom type, we can define a andMap and mapN function that will allow us to combine multiple Validatable values into a single Validatable when we need to know whether the form as a whole is valid and ready to be submitted.

How will you use your time on screen?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment