Back to Table of Contents

Type-Safe Form processing using reform

`reform` is a library for creating type-safe, composable, and validated HTML forms. It is built around applicative functors and is based on the same principles as `formlets` and `digestive-functors <= 0.2`. The core `reform` library is designed to be portable and can be used with a wide variety of Haskell web frameworks and template solutions -- though only a few options are supported at the moment. The most basic method of creating and processing forms with out the assistance of `reform` is to: 1. create a `
` tag with the desired elements by hand 2. write code which processes the form data set and tries to extract a value from it The developer will encounter a number of difficulties using this method: 1. the developer must be careful to use the same `name` field in the HTML and the code. 2. if a new field is added to the form, the code must be manually updated. Failure to do so will result in the new field being silently ignored. 3. form fragments can not be easily combined because the `name` or `id` fields might collide. Additionally, there is no simple way to combine the validation/value extraction code. 4. if the form fails to validate, it is difficult to redisplay the form with the error messages and data that was submitted. `reform` solves these problems by combining the view generation code and validation code into a single `Form` element. The `Form` elements can be safely combined to create more complex forms. In theory, `reform` could be applied to other domains, such as command-line or GUI applications. However, `reform` is based around the pattern of: 1. generate the entire form at once 2. wait until the user has filled out all the fields and submitted it 3. process the results and generate an answer or redisplay the form with validation errors For most interactive applications, there is no reason to wait until the entire form has been filled out to perform validation.

Brief History

`reform` is an extension of the OCaml-based `formlets` concept originally developed by Ezra Cooper, Sam Lindley, Philip Wadler and Jeremy Yallop. The original `formlets` code was ported to Haskell as the `formlets` library, and then revamped again as the `digestive-functors <= 0.2` library. `digestive-functors` 0.3 represents a major break from the traditional `formlets` model. The primary motivation behind `digestive-functors` 0.3 was (mostly likely) to allow the separation of validators from the view code. This allows library authors to define validation for forms, while allowing the library users to create the view for the forms. It also provides a mechanism to support templating systems like `Heist`, where the view is defined in an external XML file rather than Haskell code. In order to achieve this, `digestive-functors` 0.3 unlinks the validation and view code and requires the developers to stitch them back together using `String` based names. This, of course, leads to runtime errors. If the library author adds new required fields to the validator, the user gets no compile time warnings or errors to let them know their code is broken. The `Reform` library is a heavily modified fork of `digestive-functors` 0.2. It builds on the the traditional `formlets` safety and style and extends it to allow view and validation separation in a type-safe manner. You can find the original papers on `formlets` [here](http://groups.inf.ed.ac.uk/links/formlets/).

Hello Form!

You will need to install the following optional packages for this section:
cabal install reform reform-happstack reform-hsp
The easiest way to learn `Reform` is through example. We will start with a simple form that does not require any special validation. We will then extend the form, adding some simple validators. And then we will show how we can split the validation and view for our form into separate libraries. This example uses Happstack for the web server and HSP for the templating library. First we have some pragmas:
> {-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses > , ScopedTypeVariables, TypeFamilies, TypeSynonymInstances #-} > {-# OPTIONS_GHC -F -pgmFtrhsx #-} > module Main where >
And then some imports. We import modules from three different `reform` packages: the core `reform` library, the `reform-happstack` package, and the `reform-hsp` package:
> import Control.Applicative > import Control.Applicative.Indexed (IndexedFunctor(..), IndexedApplicative(..)) > import Control.Monad (msum) > import Happstack.Server > import Happstack.Server.HSP.HTML () > import HSP.ServerPartT > import HSP > import Text.Reform ( CommonFormError(..), Form, FormError(..), Proof(..), (++>) > , (<++), commonFormErrorStr, decimal, prove > , transformEither, transform ) > import Text.Reform.Happstack > import Text.Reform.HSP.String >
Next we will create a type alias for our application's server monad:
> type AppT m = XMLGenT (ServerPartT m) >
We will also want a function that generates a page template for our app:
> appTemplate :: ( Functor m, Monad m > , EmbedAsChild (ServerPartT m) headers > , EmbedAsChild (ServerPartT m) body > ) => > String -- ^ contents of tag > -> headers -- ^ extra content for <head> tag, use () for nothing > -> body -- ^ contents of <body> tag > -> AppT m Response > appTemplate title headers body = > toResponse <$> > <html> > <head> > <title><% title %> > <% headers %> > > > <% body %> > > >
Forms have the type `Form` which looks like:
] newtype Form m input error view proof a = Form { ... }
As you will note it is heavily parameterized:
m
a monad which can be used to validate the result
input
the framework specific type containing the fields from the form data set.
error
An application specific type for form validation errors.
view
The type of the view for the form.
proof
A datatype which names something that has been proven about the result.
a
The value returned when the form data set is successfully decoded and validated.
In order to keep our type signatures sane, it is convenient to create an application specific type alias for the `Form` type:
> type SimpleForm = Form (AppT IO) [Input] AppError [AppT IO (XMLType (ServerPartT IO))] () >
`AppError` is an application specific type used to report form validation errors:
> data AppError > = Required > | NotANatural String > | AppCFE (CommonFormError [Input]) > deriving Show >
Instead of having one error type for all the forms, we could have per-form error types -- or even just use `String`. The advantage of using a type is that it makes it easier to provide I18N translations, or for users of a library to customize the text of the error messages. The disadvantage of using a custom type over a plain `String` is that it can make it more difficult to combine forms into larger forms since they must all have the same error type. Additionally, it is a bit more work to create the error type and the `FormError` instance. We will want an `EmbedAsChild` instance so that we can easily embed the errors in our HTML:
> instance (Monad m) => EmbedAsChild (ServerPartT m) AppError where > asChild Required = asChild $ "required" > asChild (NotANatural str) = asChild $ "Could not decode as a positive integer: " ++ > str > asChild (AppCFE cfe) = asChild $ commonFormErrorStr show cfe >
The error type also needs a `FormError` instance:
> instance FormError AppError where > type ErrorInputType AppError = [Input] > commonFormError = AppCFE >
Internally, `reform` has an error type `CommonFormError` which is used to report things like missing fields and other internal errors. The `FormError` class is used to lift those errors into our custom error type. Now we have the groundwork laid to create a simple form. Let's create a form that allows users to post a message. First we will want a type to represent the message -- a simple record will do:
> data Message = Message > { name :: String -- ^ the author's name > , title :: String -- ^ the message title > , message :: String -- ^ contents of the message > } deriving (Eq, Ord, Read, Show) >
and a simple function to render the `Message` as `XML`:
> renderMessage :: (Monad m) => Message -> AppT m XML > renderMessage msg = >
>
name:
<% name msg %>
>
title:
<% title msg %>
>
message:
<% message msg %>
>
>
Now we can create a very basic form:
> postForm :: SimpleForm Message > postForm = > Message > <$> label "name:" ++> inputText "" <++ br > <*> label "title: " ++> inputText "" <++ br > <*> (label "message:" <++ br) ++> textarea 80 40 "" <++ br > <* inputSubmit "post" >
This form contains all the information needed to generate the form elements and to parse the submitted form data set and extract a `Message` value. The following functions come from `reform-hsp`. `reform-blaze` provides similar functions. * `label` function creates a `