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 :

validStreetName :: String -> Maybe Street

validStreetNo :: String -> Maybe N

validPostCode :: String -> Maybe PostCode

...

validAddress :: String -> String -> String -> Maybe (Street, N, PostCode)

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

class Functor f => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

“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 :

validAddress :: String -> String -> String -> Maybe (Street, N, PostCode)
validAddress sa sb sc = (,,) <$> validStreetName sa <*> validStreetNo sb <*> validPostCode sc

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 :

data Form a where
  FormField :: Field a -> Form a
  Pure :: a -> Form a
  App :: Form (a -> b) -> Form a -> Form b

The constructors Pure and App are used to implement the Applicative interface for Form, whereas FormField contains form elements.

data Field a where
  -- | text input field
  Text :: String -> Field String
  -- | switch (binary choice)
  Switch :: String -> Field Bool

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,

switch :: String -> Form Bool
switch ref = FormField $ Switch ref 

such that a form that made of two switches can be built up using Applicative operators as follows :

form :: Form (Bool, Bool)
form = (,) <$> switch "s0" <*> switch "s1"

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.

view :: Form a -> [Widget]
view = \case
  FormField ff -> pure $ viewField ff
  Pure _ -> pure ""
  App (Pure _) x -> view x
  App f x -> view f <> view x
  
viewField :: Field a -> Widget
viewField = \case
  Switch n -> ...

A Widget holds concrete information necessary to construct a HTML form.

The evaluation 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.

eval :: Form a
     -> (FormInputs -> Result [String] a)
eval form fi = case form of
  FormField ff -> evalField fi ff
  Pure x -> pure x
  App fm xm -> do
    f <- eval fm fi
    x <- eval xm fi
    pure $ f x
    
evalField :: FormInputs -> Field a -> Result [String] a
evalField gfi = \case
  Switch n  ->
    case lookupFormInput n gfi of
      Nothing -> pure False  -- default to False
      Just v -> if v == 'true' then pure True
        else if v == 'false' then pure False
        else
        let err = unwords ["switch '", n, "' : unexpected value", v]
            in Error [err]

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