Composable web forms with Applicative
Introduction
Interactive web applications feature a complex exchange of information between user, frontend and backend; in time the focus of this complexity has shifted from the backend (remember CGI scripts?) to the frontend (javascript, perhaps webassembly one day) and back.
Form-based interaction is an interesting subset of this problem since the input data is well structured and the problem can be formalized in some generality. One such formalization is due to Cooper et al.
What’s a web form? It’s really two things : an (HTML) view and a callback that will process the inputs submitted via that view, and possibly produce a new view as a result.
Schematically, a form can be seen as a pair (view, input -> Maybe view')
.
We would like this notion to be composable : forms are arrays of typed inputs (e.g. strings, booleans, numbers, dates), and we expect to parse the input into a corresponding arrays of output values.
This post will motivate the internals of the formlets
/digestive-functors
libraries, which provide composable HTML “formlets” and are backed by solid functional programming abstractions.
Parallel validation
As we saw above, processing user input is half of the “form problem”.
Say we want to validate a street address, composed of a street name and number and a postal code. We can think of a few rules that a good street address must satisfy : the street name should be formatted in a certain way, the number should be a positive integer and the postal code made of 5 digits.
If we write these rules as little validation functions, how can we compose these functions together back into one that validates the address as a whole?
Practically, we are looking to achieve this :
Applicative
In functional programming languages the notion of Applicative
functor represents a property of functions that perform more than one independent computation.
In Haskell, this abstraction comes in form of a typeclass with the same name (called “Idiom” originally by McBride and Paterson who discovered it).
“Star” ( <*>
) can be seen as a natural composition operator for “curried” functions that have effects, and is exactly what lets us compose validators.
Simplifying somewhat :
Applicative formlets
What’s needed now is an (embedded) language that lets us write both form validators and views in terms of the Applicative
interface. Enter the Form
DSL :
The constructors Pure
and App
are used to implement the Applicative
interface for Form
, whereas FormField
contains form elements.
The String
fields of the Field
constructors hold unique references that will be used to lookup the form inputs from the frontend request body.
We can then declare “smart constructors” for the form elements, that will be labeled with the respective output type,
such that a form that made of two switches can be built up using Applicative operators as follows :
Viewing and evaluating forms
Now we need ways to convert Form
terms into HTML views and validation callbacks respectively. Concretely, we need to write two functions, view
and eval
.
I provide two implementation sketches here, but the details depend on the actual frontend framework.
A Widget
holds concrete information necessary to construct a HTML form.
The eval
uation function similarly builds up the result by recursively unpacking the input form. Here I have parenthesized its type signature to show that a callback function is computed from a Form
, as we were hoping.
And that’s it !
Implementation Notes
You need a few language extensions to make the above work, e.g. LambdaCase
and GADTs
.
The Result
type is the same as Either
, except for its Applicative
instance that juxtaposes errors from distinct code paths together. This is conceptually identical to the Applicative
instance of Validation
.
You can make the above more type safe by getting rid of strings, making sure all references are unique etc., but here we are only concerned with a high-level view of the library.
Conclusion
Hopefully this post managed to convey the power and practicality of the Applicative interface when applied to web form-based navigation, and to show why are the internals of “formlet” libraries implemented in this way.
The ideas presented here are already consolidated into production-grade libraries, in particular the digestive-functors
suite, which comes with adapters for various web frameworks.
References
McBride, C. and Paterson, R., Idioms: applicative programming with effects
GHC Haskell base, Applicative
Cooper, E. et al., An idiom’s guide to formlets
Van der Jeugt, J. et al. digestive-functors