diff --git a/Control/Applicative/Indexed.hs b/Control/Applicative/Indexed.hs
new file mode 100644
--- /dev/null
+++ b/Control/Applicative/Indexed.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{- |
+This module provides a type-indexed / parameterized version of the 'Functor' and 'Applicative' classes.
+-}
+module Control.Applicative.Indexed where
+
+import Control.Applicative (Applicative(pure, (<*>)))
+
+------------------------------------------------------------------------------
+-- * type-indexed / parameterized classes
+------------------------------------------------------------------------------
+
+-- | a class for a 'type-indexed' or 'paramaterized' functor
+--
+-- note: not sure what the most correct name is for this class, or if
+-- it exists in a well supported library already.
+class IndexedFunctor f where
+    -- | imap is similar to fmap
+    imap :: (x -> y) -- ^ function to apply to first parameter
+         -> (a -> b) -- ^ function to apply to second parameter
+         -> f x a    -- ^ indexed functor
+         -> f y b
+
+-- | a class for a 'type-indexed' or 'paramaterized' applicative functors
+--
+-- note: not sure what the most correct name is for this class, or if
+-- it exists in a well supported library already.
+class (IndexedFunctor f) => IndexedApplicative f where
+    -- | similar to 'pure'
+    ipure   :: x -> a -> f x a
+    -- | similar to '<*>'
+    (<<*>>) :: f (x -> y) (a -> b) -> f x a -> f y b
+    -- | similar to 'Control.Applicative.*>'
+    (*>>) :: f x a -> f y b -> f y b
+    (*>>) = liftIA2 (const id) (const id)
+    -- | similar to 'Control.Applicative.<*'
+    (<<*) :: f x a -> f y b -> f x a
+    (<<*) = liftIA2 const const
+
+infixl 4 <<*>>, <<*, *>> -- , <<**>>
+
+-- | similar to 'Data.Functor.<$>'. An alias for @imap id@
+(<<$>>) :: IndexedFunctor f => (a -> b) -> f y a -> f y b
+(<<$>>) = imap id
+
+infixl 4 <<$>>
+
+-- | A variant of '<<*>>' with the arguments reversed.
+(<<**>>) :: (IndexedApplicative f) => f x a -> f (x -> y) (a -> b) -> f y b
+(<<**>>) = liftIA2 (flip ($)) (flip ($))
+
+-- | Lift a function to actions.
+-- This function may be used as a value for `imap` in a `IndexedFunctor` instance.
+liftIA :: (IndexedApplicative f) => (a -> b) -> (x -> y) -> f a x -> f b y
+liftIA f g a = ipure f g <<*>> a
+
+-- | Lift a binary function to actions.
+liftIA2 :: (IndexedApplicative f) => (a -> b -> c) -> (x -> y -> z) -> f a x -> f b y -> f c z
+liftIA2 f g a b = ipure f g <<*>> a <<*>> b
+
+-- | Lift a binary function to actions.
+liftIA3 :: (IndexedApplicative f) => (a -> b -> c -> d) -> (w -> x -> y -> z) -> f a w -> f b x -> f c y -> f d z
+liftIA3 f g a b c = ipure f g <<*>> a <<*>> b <<*>> c
+
+------------------------------------------------------------------------------
+-- * WrappedApplicative
+------------------------------------------------------------------------------
+
+-- | a wrapper which lifts a value with an 'Applicative' instance so that it can be used as an 'IndexedFunctor' or 'IndexedApplicative'
+--
+-- > d :: WrappedApplicative Maybe y Char
+-- > d = WrappedApplicative (Just succ) <<*>> WrappedApplicative (Just 'c')
+newtype WrappedApplicative f index a = WrappedApplicative { unwrapApplicative :: f a }
+    deriving (Functor, Applicative, Monad, Eq, Ord, Read, Show)
+
+instance (Functor f) => IndexedFunctor (WrappedApplicative f) where
+    imap f g (WrappedApplicative a) = WrappedApplicative (fmap g a)
+
+instance (Applicative f) => IndexedApplicative (WrappedApplicative f) where
+    ipure x a = WrappedApplicative (pure a)
+    (WrappedApplicative f) <<*>> (WrappedApplicative a) = WrappedApplicative (f <*> a)
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2012, Jeremy Shaw
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Jeremy Shaw nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Text/Reform.hs b/Text/Reform.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform.hs
@@ -0,0 +1,14 @@
+module Text.Reform
+    ( module Data.Monoid
+    , module Text.Reform.Backend
+    , module Text.Reform.Core
+    , module Text.Reform.Result
+    , module Text.Reform.Proof
+    )
+    where
+
+import Data.Monoid
+import Text.Reform.Backend
+import Text.Reform.Core
+import Text.Reform.Result
+import Text.Reform.Proof
diff --git a/Text/Reform/Backend.hs b/Text/Reform/Backend.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform/Backend.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-}
+{- |
+This module contains two classes. 'FormInput' is a class which is parameterized over the @input@ type used to represent form data in different web frameworks. There should be one instance for each framework, such as Happstack, Snap, WAI, etc.
+
+The 'FormError' class is used to map error messages into an application specific error type.
+
+-}
+module Text.Reform.Backend where
+
+import Data.Maybe            (listToMaybe)
+import           Data.Text   (Text)
+import qualified Data.Text   as T
+import Text.Reform.Result (FormId)
+
+-- | an error type used to represent errors that are common to all backends
+--
+-- These errors should only occur if there is a bug in the reform-*
+-- packages. Perhaps we should make them an 'Exception' so that we can
+-- get rid of the 'FormError' class.
+data CommonFormError input
+    = InputMissing FormId
+    | NoStringFound input
+    | NoFileFound input
+    | MultiFilesFound input
+    | MultiStringsFound input
+    | MissingDefaultValue
+      deriving (Eq, Ord, Show)
+
+-- | some default error messages for 'CommonFormError'
+commonFormErrorStr :: (input -> String)     -- ^ show 'input' in a format suitable for error messages
+                   -> CommonFormError input -- ^ a 'CommonFormError'
+                   -> String
+commonFormErrorStr showInput cfe =
+    case cfe of
+      (InputMissing formId)     -> "Input field missing for " ++ show formId
+      (NoStringFound input)     -> "Could not extract a string value from: " ++ showInput input
+      (NoFileFound input)       -> "Could not find a file associated with: " ++ showInput input
+      (MultiFilesFound input)   -> "Found multiple files associated with: " ++ showInput input
+      (MultiStringsFound input) -> "Found multiple strings associated with: " ++ showInput input
+      MissingDefaultValue       -> "Missing default value."
+
+-- | A Class to lift a 'CommonFormError' into an application-specific error type
+class FormError e where
+    type ErrorInputType e
+    commonFormError :: (CommonFormError (ErrorInputType e)) -> e
+
+-- | Class which all backends should implement.
+--
+class FormInput input where
+    -- |@input@ is here the type that is used to represent a value
+    -- uploaded by the client in the request.
+    type FileType input
+    -- | Parse the input into a string. This is used for simple text fields
+    -- among other things
+    --
+    getInputString :: (FormError error, ErrorInputType error ~ input) => input -> Either error String
+    getInputString input =
+           case getInputStrings input of
+             []  -> Left (commonFormError $ NoStringFound input)
+             [s] -> Right s
+             _   -> Left (commonFormError $ MultiStringsFound input)
+
+    -- | Should be implemented
+    --
+    getInputStrings :: input -> [String]
+
+    -- | Parse the input value into 'Text'
+    --
+    getInputText :: (FormError error, ErrorInputType error ~ input) => input -> Either error Text
+    getInputText input =
+           case getInputTexts input of
+             []  -> Left (commonFormError $ NoStringFound input)
+             [s] -> Right s
+             _   -> Left (commonFormError $ MultiStringsFound input)
+
+
+    -- | Can be overriden for efficiency concerns
+    --
+    getInputTexts :: input -> [Text]
+    getInputTexts = map T.pack . getInputStrings
+
+    -- | Get a file descriptor for an uploaded file
+    --
+    getInputFile :: (FormError error, ErrorInputType error ~ input) => input -> Either error (FileType input)
diff --git a/Text/Reform/Core.hs b/Text/Reform/Core.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform/Core.hs
@@ -0,0 +1,354 @@
+{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving #-}
+{- |
+This module defines the 'Form' type, its instances, core manipulation functions, and a bunch of helper utilities.
+-}
+module Text.Reform.Core where
+
+import Control.Applicative         (Applicative(pure, (<*>)))
+import Control.Applicative.Indexed (IndexedApplicative(ipure, (<<*>>)), IndexedFunctor (imap))
+import Control.Arrow               (first, second)
+import Control.Monad.Reader        (MonadReader(ask), ReaderT, runReaderT)
+import Control.Monad.State         (MonadState(get,put), StateT, evalStateT)
+import Control.Monad.Trans         (lift)
+import Data.Monoid                 (Monoid(mempty, mappend))
+import Text.Reform.Result       (FormId(..), FormRange(..), Result(..), unitRange, zeroId)
+
+------------------------------------------------------------------------------
+-- * Proved
+------------------------------------------------------------------------------
+
+-- | Proved records a value, the location that value came from, and something that was proved about the value.
+data Proved proofs a =
+    Proved { proofs   :: proofs
+           , pos      :: FormRange
+           , unProved :: a
+           }
+
+instance Functor (Proved ()) where
+    fmap f (Proved () pos a) = Proved () pos (f a)
+
+-- | Utility Function: trivially prove nothing about ()
+unitProved :: FormId -> Proved () ()
+unitProved formId =
+    Proved { proofs   = ()
+           , pos      = unitRange formId
+           , unProved = ()
+           }
+
+------------------------------------------------------------------------------
+-- * FormState
+------------------------------------------------------------------------------
+
+-- | inner state used by 'Form'.
+type FormState m input = ReaderT (Environment m input) (StateT FormRange m)
+
+-- | used to represent whether a value was found in the form
+-- submission data, missing from the form submission data, or expected
+-- that the default value should be used
+data Value a
+    = Default
+    | Missing
+    | Found a
+
+-- | Utility function: Get the current input
+--
+getFormInput :: Monad m => FormState m input (Value input)
+getFormInput = getFormId >>= getFormInput'
+
+-- | Utility function: Gets the input of an arbitrary 'FormId'.
+--
+getFormInput' :: Monad m => FormId -> FormState m input (Value input)
+getFormInput' id' = do
+    env <- ask
+    case env of
+      NoEnvironment -> return Default
+      Environment f ->
+          lift $ lift $ f id'
+
+-- | Utility function: Get the current range
+--
+getFormRange :: Monad m => FormState m i FormRange
+getFormRange = get
+
+-- | The environment is where you get the actual input per form.
+--
+-- The 'NoEnvironment' constructor is typically used when generating a
+-- view for a GET request, where no data has yet been submitted. This
+-- will cause the input elements to use their supplied default values.
+--
+-- Note that 'NoEnviroment' is different than supplying an empty environment.
+data Environment m input
+    = Environment (FormId -> m (Value input))
+    | NoEnvironment
+
+-- | Not quite sure when this is useful and so hard to say if the rules for combining things with Missing/Default are correct
+instance (Monoid input, Monad m) => Monoid (Environment m input) where
+    mempty = NoEnvironment
+    NoEnvironment `mappend` x = x
+    x `mappend` NoEnvironment = x
+    (Environment env1) `mappend` (Environment env2) =
+        Environment $ \id' ->
+            do r1 <- (env1 id')
+               r2 <- (env2 id')
+               case (r1, r2) of
+                 (Missing, Missing) -> return Missing
+                 (Default, Missing) -> return Default
+                 (Missing, Default) -> return Default
+                 (Found x, Found y) -> return $ Found (x `mappend` y)
+                 (Found x, _      ) -> return $ Found x
+                 (_      , Found y) -> return $ Found y
+
+-- | Utility function: returns the current 'FormId'. This will only make sense
+-- if the form is not composed
+--
+getFormId :: Monad m => FormState m i FormId
+getFormId = do
+    FormRange x _ <- get
+    return x
+
+-- | Utility function: increment the current 'FormId'.
+incFormId :: Monad m => FormState m i ()
+incFormId = do
+        FormRange _ endF1 <- get
+        put $ unitRange endF1
+
+-- | A view represents a visual representation of a form. It is composed of a
+-- function which takes a list of all errors and then produces a new view
+--
+newtype View error v = View
+    { unView :: [(FormRange, error)] -> v
+    } deriving (Monoid)
+
+instance Functor (View e) where
+    fmap f (View g) = View $ f . g
+
+------------------------------------------------------------------------------
+-- * Form
+------------------------------------------------------------------------------
+
+-- | a 'Form' contains a 'View' combined with a validation function
+-- which will attempt to extract a value from submitted form data.
+--
+-- It is highly parameterized, allowing it work in a wide variety of
+-- different configurations. You will likely want to make a type alias
+-- that is specific to your application to make type signatures more
+-- manageable.
+--
+--   [@m@] A monad which can be used by the validator
+--
+--   [@input@] A framework specific type for representing the raw key/value pairs from the form data
+--
+--   [@error@] A application specific type for error messages
+--
+--   [@view@] The type of data being generated for the view (HSP, Blaze Html, Heist, etc)
+--
+--   [@proof@] A type which names what has been proved about the return value. @()@ means nothing has been proved.
+--
+--   [@a@] Value return by form when it is successfully decoded, validated, etc.
+--
+--
+-- This type is very similar to the 'Form' type from
+-- @digestive-functors <= 0.2@. If @proof@ is @()@, then 'Form' is an
+-- applicative functor and can be used almost exactly like
+-- @digestive-functors <= 0.2@.
+newtype Form m input error view proof a = Form { unForm :: FormState m input (View error view, m (Result error (Proved proof a))) }
+
+instance (Monad m) => IndexedFunctor (Form m input view error) where
+    imap f g (Form frm) =
+        Form $ do (view, mval) <- frm
+                  val <- lift $ lift $ mval
+                  case val of
+                    (Ok (Proved p pos a)) -> return (view, return $ Ok (Proved (f p) pos (g a)))
+                    (Error errs)          -> return (view, return $ Error errs)
+
+instance (Monoid view, Monad m) => IndexedApplicative (Form m input error view) where
+    ipure p a = Form $ do i <- getFormId
+                          return (mempty, return $ Ok (Proved p (unitRange i) a))
+
+    (Form frmF) <<*>> (Form frmA) =
+        Form $ do ((view1, mfok), (view2, maok)) <- bracketState $
+                    do res1 <- frmF
+                       incFormId
+                       res2 <- frmA
+                       return (res1, res2)
+                  fok <- lift $ lift $ mfok
+                  aok <- lift $ lift $ maok
+                  case (fok, aok) of
+                     (Error errs1, Error errs2) -> return (view1 `mappend` view2, return $ Error $ errs1 ++ errs2)
+                     (Error errs1, _)           -> return (view1 `mappend` view2, return $ Error $ errs1)
+                     (_          , Error errs2) -> return (view1 `mappend` view2, return $ Error $ errs2)
+                     (Ok (Proved p (FormRange x _) f), Ok (Proved q (FormRange _ y) a)) ->
+                         return (view1 `mappend` view2, return $ Ok $ Proved { proofs   = p q
+                                                                           , pos      = FormRange x y
+                                                                           , unProved = f a
+                                                                           })
+
+bracketState :: Monad m => FormState m input a -> FormState m input a
+bracketState k = do
+    FormRange startF1 _ <- get
+    res <- k
+    FormRange _ endF2 <- get
+    put $ FormRange startF1 endF2
+    return res
+
+
+instance (Functor m) => Functor (Form m input error view ()) where
+    fmap f form =
+        Form $ fmap (second (fmap (fmap (fmap f)))) (unForm form)
+
+
+instance (Functor m, Monoid view, Monad m) => Applicative (Form m input error view ()) where
+    pure a =
+      Form $
+        do i <- getFormId
+           return (View $ const $ mempty, return $ Ok $ Proved { proofs    = ()
+                                                               , pos       = FormRange i i
+                                                               , unProved  = a
+                                                               })
+    -- this coud be defined in terms of <<*>> if we just changed the proof of frmF to (() -> ())
+    (Form frmF) <*> (Form frmA) =
+       Form $
+         do ((view1, mfok), (view2, maok)) <- bracketState $
+              do res1 <- frmF
+                 incFormId
+                 res2 <- frmA
+                 return (res1, res2)
+            fok <- lift $ lift $ mfok
+            aok <- lift $ lift $ maok
+            case (fok, aok) of
+              (Error errs1, Error errs2) -> return (view1 `mappend` view2, return $ Error $ errs1 ++ errs2)
+              (Error errs1, _)           -> return (view1 `mappend` view2, return $ Error $ errs1)
+              (_          , Error errs2) -> return (view1 `mappend` view2, return $ Error $ errs2)
+              (Ok (Proved p (FormRange x _) f), Ok (Proved q (FormRange _ y) a)) ->
+                  return (view1 `mappend` view2, return $ Ok $ Proved { proofs   = ()
+                                                                      , pos      = FormRange x y
+                                                                      , unProved = f a
+                                                                      })
+
+-- ** Ways to evaluate a Form
+
+-- | Run a form
+--
+runForm :: (Monad m) =>
+           Environment m input
+        -> String
+        -> Form m input error view proof a
+        -> m (View error view, m (Result error (Proved proof a)))
+runForm env prefix form =
+    evalStateT (runReaderT (unForm form) env) (unitRange (zeroId prefix))
+
+-- | Run a form
+--
+runForm' :: (Monad m) =>
+            Environment m input
+         -> String
+        -> Form m input error view proof a
+        -> m (view , Maybe a)
+runForm' env prefix form =
+    do (view', mresult) <- runForm env prefix form
+       result <- mresult
+       return $ case result of
+                  Error e  -> (unView view' e , Nothing)
+                  Ok x     -> (unView view' [], Just (unProved x))
+
+-- | Just evaluate the form to a view. This usually maps to a GET request in the
+-- browser.
+--
+viewForm :: (Monad m) =>
+            String                          -- ^ form prefix
+         -> Form m input error view proof a -- ^ form to view
+         -> m view
+viewForm prefix form =
+    do (v, _) <- runForm NoEnvironment prefix form
+       return (unView v [])
+
+-- | Evaluate a form
+--
+-- Returns:
+--
+-- [@Left view@] on failure. The @view@ will have already been applied to the errors.
+--
+-- [@Right a@] on success.
+--
+eitherForm :: (Monad m) =>
+              Environment m input             -- ^ Input environment
+           -> String                          -- ^ Identifier for the form
+           -> Form m input error view proof a -- ^ Form to run
+           -> m (Either view a)               -- ^ Result
+eitherForm env id' form = do
+    (view', mresult) <- runForm env id' form
+    result <- mresult
+    return $ case result of
+        Error e  -> Left $ unView view' e
+        Ok x     -> Right (unProved x)
+
+-- | create a 'Form' from some @view@.
+--
+-- This is typically used to turn markup like @\<br\>@ into a 'Form'.
+view :: (Monad m) =>
+        view                           -- ^ View to insert
+     -> Form m input error view () ()  -- ^ Resulting form
+view view' =
+  Form $
+    do i <- getFormId
+       return ( View (const view')
+              , return (Ok (Proved { proofs   = ()
+                                   , pos      = FormRange i i
+                                   , unProved = ()
+                                   })))
+
+-- | Append a unit form to the left. This is useful for adding labels or error
+-- fields.
+--
+-- The 'Forms' on the left and right hand side will share the same
+-- 'FormId'. This is useful for elements like @\<label
+-- for=\"someid\"\>@, which need to refer to the id of another
+-- element.
+(++>) :: (Monad m, Monoid view)
+      => Form m input error view () ()
+      -> Form m input error view proof a
+      -> Form m input error view proof a
+f1 ++> f2 = Form $ do
+    -- Evaluate the form that matters first, so we have a correct range set
+    (v2, r) <- unForm f2
+    (v1, _) <- unForm f1
+    return (v1 `mappend` v2, r)
+
+infixl 6 ++>
+
+-- | Append a unit form to the right. See '++>'.
+--
+(<++) :: (Monad m, Monoid view)
+      => Form m input error view proof a
+      -> Form m input error view () ()
+      -> Form m input error view proof a
+f1 <++ f2 = Form $ do
+    -- Evaluate the form that matters first, so we have a correct range set
+    (v1, r) <- unForm f1
+    (v2, _) <- unForm f2
+    return (v1 `mappend` v2, r)
+
+infixr 5 <++
+
+-- | Change the view of a form using a simple function
+--
+-- This is useful for wrapping a form inside of a \<fieldset\> or other markup element.
+mapView :: (Monad m, Functor m)
+        => (view -> view')        -- ^ Manipulator
+        -> Form m input error view  proof a  -- ^ Initial form
+        -> Form m input error view' proof a  -- ^ Resulting form
+mapView f = Form . fmap (first $ fmap f) . unForm
+
+-- | Utility Function: turn a view and return value into a successful 'FormState'
+mkOk :: (Monad m) =>
+         FormId
+      -> view
+      -> a
+      -> FormState m input (View error view, m (Result error (Proved () a)))
+mkOk i view val =
+    return ( View $ const $ view
+           , return $ Ok (Proved { proofs   = ()
+                                 , pos      = unitRange i
+                                 , unProved = val
+                                 })
+           )
diff --git a/Text/Reform/Generalized.hs b/Text/Reform/Generalized.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform/Generalized.hs
@@ -0,0 +1,283 @@
+{-# LANGUAGE ScopedTypeVariables, TypeFamilies, ViewPatterns #-}
+{- |
+This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
+-}
+module Text.Reform.Generalized where
+
+import Control.Applicative    ((<$>))
+import qualified Data.IntSet  as IS
+import Data.List              (find)
+import Data.Maybe             (mapMaybe)
+import Numeric                (readDec)
+import Text.Reform.Backend
+import Text.Reform.Core
+import Text.Reform.Result
+
+-- | used for constructing elements like @\<input type=\"text\"\>@, which return a single input value.
+input :: (Monad m, FormError error) =>
+         (input -> Either error a)
+      -> (FormId -> a -> view)
+      -> a
+      -> Form m input error view () a
+input fromInput toView initialValue =
+    Form $ do i <- getFormId
+              v <- getFormInput' i
+              case v of
+                Default ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = initialValue
+                                                 }))
+                (Found (fromInput -> (Right a))) ->
+                    return ( View $ const $ toView i a
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = a
+                                                 }))
+                (Found (fromInput -> (Left error))) ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Error [(unitRange i, error)]
+                           )
+                Missing ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Error [(unitRange i, commonFormError (InputMissing i))]
+                           )
+
+-- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data.
+inputMaybe :: (Monad m, FormError error) =>
+         (input -> Either error a)
+      -> (FormId -> a -> view)
+      -> a
+      -> Form m input error view () (Maybe a)
+inputMaybe fromInput toView initialValue =
+    Form $ do i <- getFormId
+              v <- getFormInput' i
+              case v of
+                Default ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = Just initialValue
+                                                 }))
+                (Found (fromInput -> (Right a))) ->
+                    return ( View $ const $ toView i a
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = (Just a)
+                                                 }))
+                (Found (fromInput -> (Left error))) ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Error [(unitRange i, error)]
+                           )
+                Missing ->
+                    return ( View $ const $ toView i initialValue
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = Nothing
+                                                 })
+                           )
+
+-- | used for elements like @\<input type=\"reset\"\>@ which take a value, but are never present in the form data set.
+inputNoData :: (Monad m) =>
+              (FormId -> a -> view)
+           -> a
+           -> Form m input error view () ()
+inputNoData toView a =
+    Form $ do i <- getFormId
+              return ( View $ const $ toView i a
+                     , return $ Ok (Proved { proofs   = ()
+                                           , pos      = unitRange i
+                                           , unProved = ()
+                                           })
+                     )
+
+-- | used for @\<input type=\"file\"\>@
+inputFile :: forall m input error view. (Monad m, FormInput input, FormError error, ErrorInputType error ~ input) =>
+             (FormId -> view)
+          -> Form m input error view () (FileType input)
+inputFile toView =
+    Form $ do i <- getFormId
+              v <- getFormInput' i
+              case v of
+                Default ->
+                    return ( View $ const $ toView i
+                           , return $ Error [(unitRange i, commonFormError (InputMissing i))]
+                           )
+
+                (Found (getInputFile' -> (Right a))) ->
+                    return ( View $ const $ toView i
+                           , return $ Ok (Proved { proofs   = ()
+                                                 , pos      = unitRange i
+                                                 , unProved = a
+                                                 }))
+
+                (Found (getInputFile' -> (Left error))) ->
+                    return ( View $ const $ toView i
+                           , return $ Error [(unitRange i, error)]
+                           )
+                Missing ->
+                    return ( View $ const $ toView i
+                           , return $ Error [(unitRange i, commonFormError (InputMissing i))]
+                           )
+        where
+          -- just here for the type-signature to make the type-checker happy
+          getInputFile' :: (FormError error, ErrorInputType error ~ input) => input -> Either error (FileType input)
+          getInputFile' = getInputFile
+
+-- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes
+inputMulti :: forall m input error view a lbl. (Functor m, FormError error, ErrorInputType error ~ input, FormInput input, Monad m) =>
+              [(a, lbl)]                                      -- ^ value, label, initially checked
+           -> (FormId -> [(FormId, Int, lbl, Bool)] -> view)  -- ^ function which generates the view
+           -> (a -> Bool)                                     -- ^ isChecked/isSelected initially
+           -> Form m input error view () [a]
+inputMulti choices mkView isSelected =
+    Form $ do i <- getFormId
+              inp <- getFormInput' i
+              case inp of
+
+                Default ->
+                    do let (choices', vals) = foldr (\(a, lbl) (cs,vs) ->
+                                                         if isSelected a
+                                                         then ((a, lbl, True) :cs, a:vs)
+                                                         else ((a, lbl, False):cs,   vs))
+                                                    ([],[])
+                                                    choices
+                       view     <- mkView i <$> augmentChoices choices'
+                       mkOk i view vals
+
+                Missing -> -- just means that no checkboxes were checked
+                     do view <- mkView i <$> augmentChoices (map (\(x,y) -> (x,y,False)) choices)
+                        mkOk i view []
+
+                (Found v) ->
+                    do let readDec' str = case readDec str of
+                                            [(n,[])] -> n
+                                            _ -> (-1) -- FIXME: should probably return an internal error?
+                           keys   = IS.fromList $ map readDec' $ getInputStrings v
+                           (choices', vals) =
+                               foldr (\(i, (a,lbl)) (c,v) ->
+                                          if IS.member i keys
+                                          then ((a,lbl,True) : c, a : v)
+                                          else ((a,lbl,False): c,     v)) ([],[]) $
+                                 zip [0..] choices
+                       view <- mkView i <$> augmentChoices choices'
+                       mkOk i view vals
+
+
+    where
+      augmentChoices :: (Monad m) => [(a, lbl, Bool)] -> FormState m input [(FormId, Int, lbl, Bool)]
+      augmentChoices choices = mapM augmentChoice (zip [0..] choices)
+
+      augmentChoice :: (Monad m) => (Int, (a, lbl, Bool)) -> FormState m input (FormId, Int, lbl, Bool)
+      augmentChoice (vl, (a, lbl, checked)) =
+          do i <- getFormId
+             return (i, vl, lbl, checked)
+
+
+-- | radio buttons, single @\<select\>@ boxes
+inputChoice :: forall a m error input lbl view. (Functor m, FormError error, ErrorInputType error ~ input, FormInput input, Monad m) =>
+               (a -> Bool)                                     -- ^ is default
+            -> [(a, lbl)]                                      -- ^ value, label
+            -> (FormId -> [(FormId, Int, lbl, Bool)] -> view)  -- ^ function which generates the view
+            -> Form m input error view () a
+inputChoice isDefault choices mkView =
+    Form $ do i <- getFormId
+              inp <- getFormInput' i
+
+              case inp of
+                Default ->
+                    do let (choices', def) = markSelected choices
+                       view <- mkView i <$> augmentChoices choices'
+                       mkOk' i view def
+
+                Missing -> -- can happen if no choices where checked
+                    do let (choices', def) = markSelected choices
+                       view <- mkView i <$> augmentChoices choices'
+                       mkOk' i view def
+
+                (Found v) ->
+                    do let readDec' str = case readDec str of
+                                            [(n,[])] -> n
+                                            _ -> (-1) -- FIXME: should probably return an internal error?
+                           (Right str) = getInputString v :: Either error String -- FIXME
+                           key = readDec' str
+                           (choices', mval) =
+                               foldr (\(i, (a, lbl)) (c, v) ->
+                                          if i == key
+                                          then ((a,lbl,True) : c, Just a)
+                                          else ((a,lbl,False): c,     v))
+                                     ([], Nothing) $
+                                     zip [0..] choices
+                       view <- mkView i <$> augmentChoices choices'
+                       case mval of
+                         Nothing ->
+                             return ( View $ const $ view
+                                    , return $ Error [(unitRange i, commonFormError (InputMissing i))]
+                                    )
+                         (Just val) -> mkOk i view val
+
+    where
+      mkOk' i view (Just val) = mkOk i view val
+      mkOk' i view Nothing =
+          return ( View $ const $ view
+                 , return $ Error [(unitRange i, commonFormError MissingDefaultValue)]
+                 )
+
+      markSelected :: [(a,lbl)] -> ([(a, lbl, Bool)], Maybe a)
+      markSelected cs = foldr (\(a,lbl) (vs, ma) ->
+                                   if isDefault a
+                                      then ((a,lbl,True):vs , Just a)
+                                      else ((a,lbl,False):vs, ma))
+                         ([], Nothing)
+                         cs
+
+      augmentChoices :: (Monad m) => [(a, lbl, Bool)] -> FormState m input [(FormId, Int, lbl, Bool)]
+      augmentChoices choices = mapM augmentChoice (zip [0..] choices)
+
+      augmentChoice :: (Monad m) => (Int, (a, lbl, Bool)) -> FormState m input (FormId, Int, lbl, Bool)
+      augmentChoice (vl, (a, lbl,selected)) =
+          do i <- getFormId
+             return (i, vl, lbl, selected)
+
+-- | used to create @\<label\>@ elements
+label :: Monad m =>
+         (FormId -> view)
+      -> Form m input error view () ()
+label f = Form $ do
+    id' <- getFormId
+    return ( View (const $ f id')
+           , return (Ok $ Proved { proofs   = ()
+                                 , pos      = unitRange id'
+                                 , unProved = ()
+                                 })
+           )
+-- | used to add a list of error messages to a 'Form'
+--
+-- This function automatically takes care of extracting only the
+-- errors that are relevent to the form element it is attached to via
+-- '<++' or '++>'.
+errors :: Monad m =>
+          ([error] -> view) -- ^ function to convert the error messages into a view
+       -> Form m input error view () ()
+errors f = Form $ do
+    range <- getFormRange
+    return ( View (f . retainErrors range)
+           , return (Ok $ Proved { proofs   = ()
+                                 , pos      = range
+                                 , unProved = ()
+                                 })
+           )
+
+-- | similar to 'errors' but includes error messages from children of the form as well.
+childErrors :: Monad m =>
+               ([error] -> view)
+            -> Form m input error view () ()
+childErrors f = Form $ do
+    range <- getFormRange
+    return (View (f . retainChildErrors range)
+           , return (Ok $ Proved { proofs   = ()
+                                 , pos      = range
+                                 , unProved = ()
+                                 })
+           )
diff --git a/Text/Reform/Proof.hs b/Text/Reform/Proof.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform/Proof.hs
@@ -0,0 +1,134 @@
+{- |
+This module defines the 'Proof' type, some proofs, and some helper functions.
+
+A 'Proof' does three things:
+
+ - verifies that the input value meets some criteria
+ - optionally transforms the input value to another value while preserving that criteria
+ - puts the proof name in type-signature where the type-checker can use it
+-}
+module Text.Reform.Proof where
+
+import Control.Applicative.Indexed (IndexedFunctor(imap))
+import Control.Monad.Trans   (lift)
+import Numeric               (readDec, readFloat, readSigned)
+import Text.Reform.Result (FormRange, Result(..))
+import Text.Reform.Core   (Form(..), Proved(..))
+
+-- | A 'Proof' attempts to prove something about a value.
+--
+-- If successful, it can also transform the value to a new value. The
+-- proof should hold for the new value as well.
+--
+-- Generally, each 'Proof' has a unique data-type associated with it
+-- which names the proof, such as:
+--
+-- > data NotNull = NotNull
+--
+data Proof m error proof a b
+    = Proof { proofName     :: proof                   -- ^ name of the thing to prove
+            , proofFunction :: a -> m (Either error b) -- ^ function which provides the proof
+            }
+
+-- | apply a 'Proof' to a 'Form'
+prove :: (Monad m) =>
+         Form m input error view q a
+      -> Proof m error proof a b
+      -> Form m input error view proof b
+prove (Form frm) (Proof p f) =
+    Form $ do (xml, mval) <- frm
+              val <- lift $ lift $ mval
+              case val of
+                (Error errs)            -> return (xml, return $ Error errs)
+                (Ok (Proved _ pos a)) ->
+                    do r <- lift $ lift $ f a
+                       case r of
+                         (Left err) -> return (xml, return $ Error [(pos, err)])
+                         (Right b)  ->
+                             return (xml, return $ Ok (Proved { proofs   = p
+                                                              , pos      = pos
+                                                              , unProved = b
+                                                              }))
+
+-- * transformations (proofs minus the proof).
+
+-- | transform a 'Form' using a 'Proof', and the replace the proof with @()@.
+--
+-- This is useful when you want just want classic digestive-functors behaviour.
+transform :: (Monad m) =>
+             Form m input error view anyProof a
+          -> Proof m error proof a b
+          -> Form m input error view () b
+transform frm proof = imap (const ()) id (frm `prove` proof)
+
+-- | transform the 'Form' result using a monadic 'Either' function.
+transformEitherM :: (Monad m) => Form m input error view anyProof a
+                 -> (a -> m (Either error b))
+                 -> Form m input error view () b
+transformEitherM frm func = frm `transform` (Proof () func)
+
+-- | transform the 'Form' result using an 'Either' function.
+transformEither :: (Monad m) =>
+                   Form m input error view anyProof a
+                -> (a -> Either error b)
+                -> Form m input error view () b
+transformEither frm func = transformEitherM frm (return . func)
+
+-- * Various Proofs
+
+-- | proof that a list is not empty
+data NotNull = NotNull
+
+-- | prove that a list is not empty
+notNullProof :: (Monad m) => error -> Proof m error NotNull [a] [a]
+notNullProof errorMsg = Proof NotNull (return . check)
+    where
+    check list =
+        if null list
+          then (Left errorMsg)
+          else (Right list)
+
+-- | proof that a 'String' is a decimal number
+data Decimal        = Decimal
+-- | proof that a 'String' is a Real/Fractional number
+data RealFractional = RealFractional
+-- | proof that a number is also (allowed to be) signed
+data Signed a       = Signed a
+
+-- | read an unsigned number in decimal notation
+decimal :: (Monad m, Eq i, Num i) =>
+           (String -> error) -- ^ create an error message ('String' is the value that did not parse)
+        -> Proof m error Decimal String i
+decimal mkError = Proof Decimal (return . toDecimal)
+    where
+      toDecimal str =
+          case readDec str of
+            [(d,[])] -> (Right d)
+            _        -> (Left $ mkError str)
+
+-- | read signed decimal number
+signedDecimal :: (Monad m, Eq i, Real i) => (String -> error) -> Proof m error (Signed Decimal) String i
+signedDecimal mkError = Proof (Signed Decimal) (return . toDecimal)
+    where
+      toDecimal str =
+          case (readSigned readDec) str of
+            [(d,[])] -> (Right d)
+            _        -> (Left $ mkError str)
+
+-- | read 'RealFrac' number
+realFrac :: (Monad m, RealFrac a) => (String -> error) -> Proof m error RealFractional String a
+realFrac mkError = Proof RealFractional (return . toRealFrac)
+    where
+      toRealFrac str =
+          case readFloat str of
+            [(f,[])] -> (Right f)
+            _        -> (Left $ mkError str)
+
+-- | read a signed 'RealFrac' number
+realFracSigned :: (Monad m, RealFrac a) => (String -> error) -> Proof m error (Signed RealFractional) String a
+realFracSigned mkError = Proof (Signed RealFractional) (return . toRealFrac)
+    where
+      toRealFrac str =
+          case (readSigned readFloat) str of
+            [(f,[])] -> (Right f)
+            _        -> (Left $ mkError str)
diff --git a/Text/Reform/Result.hs b/Text/Reform/Result.hs
new file mode 100644
--- /dev/null
+++ b/Text/Reform/Result.hs
@@ -0,0 +1,120 @@
+-- | Module for the core result type, and related functions
+--
+module Text.Reform.Result
+    ( Result (..)
+    , getResult
+    , FormId
+    , zeroId
+    , mapId
+    , formIdList
+    , FormRange (..)
+    , incrementFormId
+    , unitRange
+    , isInRange
+    , isSubRange
+    , retainErrors
+    , retainChildErrors
+    ) where
+
+import Control.Applicative (Applicative (..))
+import Data.List (intercalate)
+
+-- | Type for failing computations
+--
+data Result e ok
+    = Error [(FormRange, e)]
+    | Ok ok
+      deriving (Show, Eq)
+
+instance Functor (Result e) where
+    fmap _ (Error x) = Error x
+    fmap f (Ok x)    = Ok (f x)
+
+instance Monad (Result e) where
+    return = Ok
+    Error x >>= _ = Error x
+    Ok x    >>= f = f x
+
+instance Applicative (Result e) where
+    pure = Ok
+    Error x <*> Error y = Error $ x ++ y
+    Error x <*> Ok _    = Error x
+    Ok _    <*> Error y = Error y
+    Ok x    <*> Ok y    = Ok $ x y
+
+-- | convert a 'Result' to 'Maybe' discarding the error message on 'Error'
+getResult :: Result e ok -> Maybe ok
+getResult (Error _) = Nothing
+getResult (Ok r)    = Just r
+
+-- | An ID used to identify forms
+--
+data FormId = FormId
+    { -- | Global prefix for the form
+      formPrefix :: String
+    , -- | Stack indicating field. Head is most specific to this item
+      formIdList :: [Integer]
+    } deriving (Eq, Ord)
+
+-- | The zero ID, i.e. the first ID that is usable
+--
+zeroId :: String -> FormId
+zeroId p = FormId
+    { formPrefix = p
+    , formIdList = [0]
+    }
+
+-- | map a function over the @[Integer]@ inside a 'FormId'
+mapId :: ([Integer] -> [Integer]) -> FormId -> FormId
+mapId f (FormId p is) = FormId p $ f is
+
+instance Show FormId where
+    show (FormId p xs) =
+        p ++ "-fval[" ++ (intercalate "." $ reverse $ map show xs) ++ "]"
+
+-- | get the head 'Integer' from a 'FormId'
+formId :: FormId -> Integer
+formId = head . formIdList
+
+-- | A range of ID's to specify a group of forms
+--
+data FormRange
+    = FormRange FormId FormId
+      deriving (Eq, Show)
+
+-- | Increment a form ID
+--
+incrementFormId :: FormId -> FormId
+incrementFormId (FormId p (x:xs)) = FormId p $ (x + 1):xs
+incrementFormId (FormId _ [])     = error "Bad FormId list"
+
+-- | create a 'FormRange' from a 'FormId'
+unitRange :: FormId -> FormRange
+unitRange i = FormRange i $ incrementFormId i
+
+-- | Check if a 'FormId' is contained in a 'FormRange'
+--
+isInRange :: FormId     -- ^ Id to check for
+          -> FormRange  -- ^ Range
+          -> Bool       -- ^ If the range contains the id
+isInRange a (FormRange b c) = formId a >= formId b && formId a < formId c
+
+-- | Check if a 'FormRange' is contained in another 'FormRange'
+--
+isSubRange :: FormRange  -- ^ Sub-range
+           -> FormRange  -- ^ Larger range
+           -> Bool       -- ^ If the sub-range is contained in the larger range
+isSubRange (FormRange a b) (FormRange c d) =
+    formId a >= formId c &&
+    formId b <= formId d
+
+-- | Select the errors for a certain range
+--
+retainErrors :: FormRange -> [(FormRange, e)] -> [e]
+retainErrors range = map snd . filter ((== range) . fst)
+
+-- | Select the errors originating from this form or from any of the children of
+-- this form
+--
+retainChildErrors :: FormRange -> [(FormRange, e)] -> [e]
+retainChildErrors range = map snd . filter ((`isSubRange` range) . fst)
diff --git a/reform.cabal b/reform.cabal
new file mode 100644
--- /dev/null
+++ b/reform.cabal
@@ -0,0 +1,35 @@
+Name:                reform
+Version:             0.1.1
+Synopsis:            reform is an HTML form generation and validation library
+Description:          reform follows in the footsteps of formlets and
+                      digestive-functors <= 0.2. It provides a
+                      type-safe and composable method for generating
+                      an HTML form that includes validation.
+License:             BSD3
+License-file:        LICENSE
+Author:              Jeremy Shaw, Jasper Van der Jeugt <m@jaspervdj.be>
+Maintainer:          Jeremy Shaw <jeremy@n-heptane.com>
+Copyright:           2012 Jeremy Shaw, Jasper Van der Jeugt, SeeReason Partners LLC
+Category:            Web
+Build-type:          Simple
+Homepage:            http://www.happstack.com/
+Cabal-version:       >=1.6
+
+source-repository head
+    type:     darcs
+    subdir:   reform
+    location: http://patch-tag.com/r/stepcut/reform
+
+Library
+  Exposed-modules:     Control.Applicative.Indexed
+                       Text.Reform
+                       Text.Reform.Backend
+                       Text.Reform.Core
+                       Text.Reform.Generalized
+                       Text.Reform.Proof
+                       Text.Reform.Result
+  Build-depends:       base             >= 4 && <5,
+                       containers       == 0.4.*,
+                       mtl              >= 2.0 && < 2.2,
+                       text             == 0.11.*
+
