diff --git a/ditto.cabal b/ditto.cabal
--- a/ditto.cabal
+++ b/ditto.cabal
@@ -1,5 +1,5 @@
 Name:          ditto
-Version:       0.3.1
+Version:       0.4
 Synopsis:      ditto is a type-safe HTML form generation and validation library
 Description:   ditto follows in the footsteps of formlets and
                digestive-functors <= 0.2. It provides a
@@ -24,12 +24,12 @@
   ghc-options: -Wall
   exposed-modules: 
     Ditto
-    Ditto.Backend
     Ditto.Core
-    Ditto.Generalized
-    Ditto.Generalized.Named
+    Ditto.Types
+    Ditto.Backend
     Ditto.Proof
-    Ditto.Result
+    Ditto.Generalized.Named
+    Ditto.Generalized.Unnamed
   other-modules:
     Ditto.Generalized.Internal
   build-depends:       
@@ -38,5 +38,6 @@
     , mtl        >= 2.0  && < 2.3
     , semigroups >= 0.16 && < 0.20
     , text       >= 0.11 && < 1.3
+    , torsor     <  0.2
   hs-source-dirs:      src
 
diff --git a/src/Ditto.hs b/src/Ditto.hs
--- a/src/Ditto.hs
+++ b/src/Ditto.hs
@@ -1,12 +1,9 @@
-module Ditto
-    ( module Ditto.Backend
-    , module Ditto.Core
-    , module Ditto.Result
-    , module Ditto.Proof
-    )
-    where
+-- | Reexports important modules of @ditto@
+module Ditto 
+  ( module P
+  ) where
 
-import Ditto.Backend
-import Ditto.Core
-import Ditto.Result
-import Ditto.Proof
+import Ditto.Backend as P
+import Ditto.Core as P
+import Ditto.Proof as P
+import Ditto.Types as P
diff --git a/src/Ditto/Backend.hs b/src/Ditto/Backend.hs
--- a/src/Ditto/Backend.hs
+++ b/src/Ditto/Backend.hs
@@ -1,18 +1,20 @@
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE 
+    MultiParamTypeClasses
+  , TypeFamilies
+  , OverloadedStrings
+  , FunctionalDependencies
+#-}
 
 {- |
 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 Ditto.Backend where
 
 import Data.Text (Text)
-import Ditto.Result (FormId)
+import Ditto.Types (FormId, encodeFormId)
 import qualified Data.Text as T
 
 -- | an error type used to represent errors that are common to all backends
@@ -35,7 +37,7 @@
   -> CommonFormError input -- ^ a 'CommonFormError'
   -> String
 commonFormErrorStr showInput cfe = case cfe of
-  InputMissing formId -> "Input field missing for " ++ show formId
+  InputMissing formId -> "Input field missing for " ++ (T.unpack . encodeFormId) 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
@@ -48,7 +50,7 @@
   -> CommonFormError input -- ^ a 'CommonFormError'
   -> Text
 commonFormErrorText showInput cfe = case cfe of
-  InputMissing formId -> "Input field missing for " <> (T.pack $ show formId)
+  InputMissing formId -> "Input field missing for " <> encodeFormId 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
@@ -80,9 +82,8 @@
       [s] -> Right s
       _ -> Left (commonFormError $ MultiStringsFound input)
 
-  -- | Should be implemented
-  --
   getInputStrings :: input -> [String]
+  getInputStrings = map T.unpack . getInputTexts
 
   -- | Parse the input value into 'Text'
   --
@@ -93,11 +94,10 @@
       [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 input err) => input -> Either err (FileType input)
+
diff --git a/src/Ditto/Core.hs b/src/Ditto/Core.hs
--- a/src/Ditto/Core.hs
+++ b/src/Ditto/Core.hs
@@ -1,295 +1,335 @@
-{-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE 
+    DeriveFunctor
+  , FlexibleInstances
+  , FunctionalDependencies
+  , GeneralizedNewtypeDeriving
+  , NamedFieldPuns
+  , OverloadedStrings
+  , RankNTypes
+  , ScopedTypeVariables
+  , StandaloneDeriving
+  , TypeFamilies
+  , LiberalTypeSynonyms
+  , TypeSynonymInstances
+  , UndecidableInstances
+  , DataKinds
+  , KindSignatures
+#-}
 
-{- |
-This module defines the 'Form' type, its instances, core manipulation functions, and a bunch of helper utilities.
--}
-module Ditto.Core where
+-- | The core module for @ditto@. 
+--
+-- This module provides the @Form@ type and helper functions 
+-- for constructing typesafe forms inside arbitrary "views" / web frameworks.
+-- @ditto@ is meant to be a generalized formlet library used to write
+-- formlet libraries specific to a web / gui framework
+module Ditto.Core (
+  -- * Form types
+  -- | The representation of formlets
+    FormState
+  , Form(..)
+  -- * Environment
+  -- | The interface to a given web framework
+  , Environment(..)
+  , NoEnvironment(..)
+  , WithEnvironment(..)
+  , noEnvironment
+  -- * Utility functions
+  , (@$)
+  , catchFormError
+  , catchFormErrorM
+  , eitherForm
+  , getFormId
+  , getFormInput
+  , getFormInput'
+  , getFormRange
+  , getNamedFormId
+  , incrementFormId
+  , isInRange
+  , mapFormMonad
+  , mapResult
+  , mapView
+  , mkOk
+  , retainChildErrors
+  , retainErrors
+  , runForm
+  , runForm_
+  , successDecode
+  , unitRange
+  , view
+  , viewForm
+  , pureRes
+  , liftForm
+  ) where
 
-import Control.Applicative (Applicative(..), Alternative(..))
-import Control.Monad.Reader (MonadReader (ask), ReaderT, runReaderT)
-import Control.Monad.State (MonadState (get, put), StateT, evalStateT)
-import Control.Monad.Trans (lift)
-import Data.Bifunctor (Bifunctor (..))
-import Data.List.NonEmpty (NonEmpty(..))
-import Data.Monoid (Monoid (mappend, mempty))
-import Data.Text.Lazy (Text, unpack)
-import Ditto.Result (FormId (..), FormRange (..), Result (..), unitRange, zeroId)
+import Control.Applicative
+import Control.Monad.Reader
+import Control.Monad.State.Lazy
+import Data.Bifunctor
+import Data.Text (Text)
+import Ditto.Types
+import Ditto.Backend
+import Torsor
 
 ------------------------------------------------------------------------------
--- * Proved
+-- Form types
 ------------------------------------------------------------------------------
 
--- | Proved records a value, the location that value came from, and something that was proved about the value.
-data Proved a = Proved
-  { pos :: FormRange
-  , unProved :: a
-  }
-  deriving (Show, Functor)
+-- | The Form's state is just the range of identifiers so far
+type FormState m = StateT FormRange m
 
--- | Utility Function: trivially prove nothing about ()
-unitProved :: FormId -> Proved ()
-unitProved formId =
-  Proved
-    { pos = unitRange formId
-    , unProved = ()
-    }
+-- | @ditto@'s representation of a formlet
+data Form m input err view a = Form 
+  { formDecodeInput :: input -> m (Either err a) -- ^ Decode the value from the input
+  , formInitialValue :: m a -- ^ The initial value
+  , formFormlet :: FormState m (View err view, Result err (Proved a)) -- ^ A @FormState@ which produced a @View@ and a @Result@
+  } deriving (Functor)
 
-------------------------------------------------------------------------------
--- * FormState
-------------------------------------------------------------------------------
+instance (Monad m, Monoid view) => Applicative (Form m input err view) where
 
--- | inner state used by 'Form'.
-type FormState m input = ReaderT (Environment m input) (StateT FormRange m)
+  pure x = Form (successDecode x) (pure x) $ do
+    i <- getFormId
+    pure  ( mempty
+          , Ok $ Proved
+              { pos = FormRange i i
+              , unProved = x
+              }
+          )
 
--- | 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
+  (Form df ivF frmF) <*> (Form da ivA frmA) =
+    Form 
+      ( \inp -> do 
+        f <- df inp
+        x <- da inp
+        pure (f <*> x) ) 
+      (ivF <*> ivA) 
+      ( do
+        ((view1, fok), (view2, aok)) <-
+          bracketState $ do
+            res1 <- frmF
+            incrementFormRange
+            res2 <- frmA
+            pure (res1, res2)
+        case (fok, aok) of
+          (Error errs1, Error errs2) -> pure (view1 <> view2, Error $ errs1 ++ errs2)
+          (Error errs1, _) -> pure (view1 <> view2, Error errs1)
+          (_, Error errs2) -> pure (view1 <> view2, Error errs2)
+          (Ok (Proved (FormRange x _) f), Ok (Proved (FormRange _ y) a)) ->
+            pure
+              ( view1 <> view2
+              , Ok $ Proved
+                { pos = FormRange x y
+                , unProved = f a
+                }
+              )
+      )
 
--- | Utility function: Get the current input
---
-getFormInput :: Monad m => FormState m input (Value input)
-getFormInput = getFormId >>= getFormInput'
+  f1 *> f2 = Form (formDecodeInput f2) (formInitialValue f2) $ do
+    -- Evaluate the form that matters first, so we have a correct range set
+    (v2, r) <- formFormlet f2
+    (v1, _) <- formFormlet f1
+    pure (v1 <> v2, r)
 
--- | 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 -> pure Default
-    Environment f -> lift $ lift $ f id'
+  f1 <* f2 = Form (formDecodeInput f1) (formInitialValue f1) $ do
+    -- Evaluate the form that matters first, so we have a correct range set
+    (v1, r) <- formFormlet f1
+    (v2, _) <- formFormlet f2
+    pure (v1 <> v2, r)
 
--- | Utility function: Get the current range
---
-getFormRange :: Monad m => FormState m i FormRange
-getFormRange = get
+instance (Environment m input, Monoid view, FormError input err) => Monad (Form m input err view) where
+  form >>= f =
+    let mres = fmap snd $ runForm "" form 
+    in Form
+      (\input -> do
+        res <- mres
+        case res of
+          Error {} -> do
+            iv <- formInitialValue form
+            formDecodeInput (f iv) input
+          Ok (Proved _ x) -> formDecodeInput (f x) input
+      ) 
+      (do 
+        res <- mres
+        case res of
+          Error {} -> do
+            iv <- formInitialValue form
+            formInitialValue $ f iv
+          Ok (Proved _ x) -> formInitialValue (f x)
+      )
+      (do
+        (View viewF0, res0) <- formFormlet form
+        case res0 of
+          Error errs0 -> do 
+            iv <- lift $ formInitialValue form
+            (View viewF, res) <- formFormlet $ f iv
+            let errs = case res of
+                  Error es -> es
+                  Ok {} -> []
+            pure (View $ const $ viewF0 errs0 <> viewF errs, Error (errs0 <> errs))
+          Ok (Proved _ x) -> fmap (first (\(View v) -> View $ \e -> viewF0 [] <> v e)) $ formFormlet (f x)
+      )
+  return = pure
+  (>>) = (*>) -- way more efficient than the default
 
--- | 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
+instance (Monad m, Monoid view, Semigroup a) => Semigroup (Form m input err view a) where
+  (<>) = liftA2 (<>)
 
-instance (Semigroup input, Monad m) => Semigroup (Environment m input) where
-  NoEnvironment <> x = x
-  x <> NoEnvironment = x
-  (Environment env1) <> (Environment env2) =
-    Environment $ \id' -> do
-      r1 <- (env1 id')
-      r2 <- (env2 id')
-      case (r1, r2) of
-        (Missing, Missing) -> pure Missing
-        (Default, Missing) -> pure Default
-        (Missing, Default) -> pure Default
-        (Default, Default) -> pure Default
-        (Found x, Found y) -> pure $ Found (x <> y)
-        (Found x, _) -> pure $ Found x
-        (_, Found y) -> pure $ Found y
+instance (Monad m, Monoid view, Monoid a) => Monoid (Form m input err view a) where
+  mempty = pure mempty
 
--- | Not quite sure when this is useful and so hard to say if the rules for combining things with Missing/Default are correct
-instance (Semigroup input, Monad m) => Monoid (Environment m input) where
-  mempty = NoEnvironment
-  mappend = (<>)
+instance Functor m => Bifunctor (Form m input err) where
+  first = mapView
+  second = fmap
 
--- | 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
-  pure x
+errorInitialValue :: String
+errorInitialValue = "ditto: Ditto.Core.errorInitialValue was evaluated"
 
-getNamedFormId :: Monad m => String -> FormState m i FormId
-getNamedFormId name = do
-  FormRange x _ <- get
-  pure $ case x of
-    FormIdCustom _ i -> FormIdCustom name i
-    FormId _ (i :| _) -> FormIdCustom name i
+instance (Monad m, Monoid view, FormError input err, Environment m input) => Alternative (Form m input err view) where
+  empty = Form 
+    failDecodeMDF
+    (error errorInitialValue)
+    (pure (mempty, Error []))
+  formA <|> formB = do
+    efA <- formEither formA
+    case efA of
+      Right{} -> formA
+      Left{} -> formB
 
--- | Utility function: increment the current 'FormId'.
-incFormId :: Monad m => FormState m i ()
-incFormId = do
-  FormRange _ endF1 <- get
-  put $ unitRange endF1
+------------------------------------------------------------------------------
+-- Environment
+------------------------------------------------------------------------------
 
--- | 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 err v
-  = View { unView :: [(FormRange, err)] -> v }
-  deriving (Semigroup, Monoid, Functor)
+-- | The environment typeclass: the interface between ditto and a given framework
+class Monad m => Environment m input | m -> input where
+  environment :: FormId -> m (Value input)
 
+-- | Run the form, but always return the initial value
+newtype NoEnvironment input m a = NoEnvironment { getNoEnvironment ::  m a }
+  deriving (Monad, Functor, Applicative)
+
+instance Monad m => Environment (NoEnvironment input m) input where
+  environment = noEnvironment
+
+-- | @environment@ which will always return the initial value
+noEnvironment :: Applicative m => FormId -> m (Value input)
+noEnvironment = const (pure Default)
+
+-- | Run the form, but with a given @environment@ function
+newtype WithEnvironment input m a = WithEnvironment { getWithEnvironment :: ReaderT (FormId -> m (Value input)) m a }
+  deriving (Monad, Functor, Applicative)
+
+deriving instance Monad m => MonadReader (FormId -> m (Value input)) (WithEnvironment input m)
+
+instance MonadTrans (WithEnvironment input) where
+  lift = WithEnvironment . lift
+
+instance Monad m => Environment (WithEnvironment input m) input where
+  environment fid = do
+    f <- ask
+    lift $ f fid
+
 ------------------------------------------------------------------------------
--- * Form
+-- Utility functions
 ------------------------------------------------------------------------------
 
--- | 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
---
---   [@err@] A application specific type for err 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 pure value. @()@ means nothing has been proved.
---
---   [@a@] Value pure 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 err view a = Form {unForm :: FormState m input (View err view, m (Result err (Proved a)))}
-  deriving (Functor)
+failDecodeMDF :: forall m input err a. (Applicative m, FormError input err) => input -> m (Either err a)
+failDecodeMDF = const $ pure $ Left err
+  where
+  mdf :: CommonFormError input
+  mdf = MissingDefaultValue
+  err :: err
+  err = commonFormError mdf
 
-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
-  pure res
+-- | Always succeed decoding
+successDecode :: Applicative m => a -> (input -> m (Either err a))
+successDecode = const . pure . Right
 
-instance (Functor m, Monoid view, Monad m) => Applicative (Form m input err view) where
-  pure a =
-    Form $ do
-      i <- getFormId
-      pure
-        ( View $ const $ mempty
-        , pure $ Ok $ Proved
-          { pos = FormRange i i
-          , unProved = a
-          }
-        )
+-- | Common operations on @Form@s
 
-  (Form frmF) <*> (Form frmA) =
-    Form $ do
-      ((view1, mfok), (view2, maok)) <-
-        bracketState $ do
-          res1 <- frmF
-          incFormId
-          res2 <- frmA
-          pure (res1, res2)
-      fok <- lift $ lift $ mfok
-      aok <- lift $ lift $ maok
-      case (fok, aok) of
-        (Error errs1, Error errs2) -> pure (view1 <> view2, pure $ Error $ errs1 ++ errs2)
-        (Error errs1, _) -> pure (view1 <> view2, pure $ Error $ errs1)
-        (_, Error errs2) -> pure (view1 <> view2, pure $ Error $ errs2)
-        (Ok (Proved (FormRange x _) f), Ok (Proved (FormRange _ y) a)) ->
-          pure
-            ( view1 <> view2
-            , pure $ Ok $ Proved
-              { pos = FormRange x y
-              , unProved = f a
-              }
-            )
+-- | 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 :: (Functor m)
+  => (view -> view') -- ^ Manipulator
+  -> Form m input err view a -- ^ Initial form
+  -> Form m input err view' a -- ^ Resulting form
+mapView f Form{formDecodeInput, formInitialValue, formFormlet} = 
+  Form formDecodeInput formInitialValue (fmap (first (fmap f)) formFormlet)
 
-instance (Monad m, Monoid view) => Alternative (Form m input err view) where
-  empty = Form $ pure (mempty, pure $ Error mempty)
-  formA <|> formB = Form $ do
-    (_, mres0) <- unForm formA
-    res0 <- lift $ lift mres0
-    case res0 of
-      Ok _ -> unForm formA
-      Error _ -> unForm formB
+-- | Increment a form ID
+incrementFormId :: FormId -> FormId
+incrementFormId fid = add 1 fid
 
-instance Functor m => Bifunctor (Form m input err) where
-  first = mapView
-  second = fmap
+-- | 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) = 
+     formIdentifier a >= formIdentifier b 
+  && formIdentifier a < formIdentifier c
 
-instance (Monad m, Monoid view, Semigroup a) => Semigroup (Form m input err view a) where
-  (<>) = liftA2 (<>)
+-- | 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) =
+     formIdentifier a >= formIdentifier c 
+  && formIdentifier b <= formIdentifier d
 
-instance (Monoid view, Monad m, Semigroup a) => Monoid (Form m input err view a) where
-  mempty = Form $ pure (mempty, pure $ Error mempty)
+-- | Get a @FormId@ from the FormState
+getFormId :: Monad m => FormState m FormId
+getFormId = do
+  FormRange x _ <- get
+  pure x
 
--- | This provides a Monad instance which will stop rendering on err.
---   This instance isn't a part of @Form@ because of its undesirable behavior.
---   @-XApplicativeDo@ is generally preferred
-newtype MForm m input err view a = MForm { runMForm :: Form m input err view a }
-  deriving (Functor, Bifunctor, Alternative, Applicative)
+-- | Utility function: Get the current range
+getFormRange :: Monad m => FormState m FormRange
+getFormRange = get
 
-instance (Monad m, Monoid view) => Monad (MForm m input err view) where
-  (MForm formA) >>= formFunction = MForm $ Form $ do
-    (view0, mfok) <- unForm formA
-    fok :: Result err (Proved a) <- lift $ lift mfok
-    case fok of
-      Ok x -> do
-        (view1, mfok1) <- unForm $ runMForm $ formFunction $ unProved x
-        pure
-          ( view0 <> view1
-          , mfok1
-          )
-      Error errs -> pure (view0, pure $ Error errs) 
+-- | Get a @FormIdName@ from the FormState
+getNamedFormId :: Monad m => Text -> FormState m FormId
+getNamedFormId name = do
+  FormRange x _ <- get
+  pure $ FormIdName name $ formIdentifier x
 
-runAsMForm
-  :: (Monad m)
-  => Environment m input
-  -> Text
-  -> Form m input err view a
-  -> m (View err view, m (Result err (Proved a)))
-runAsMForm env prefix' = runForm env prefix' . runMForm . MForm
+-- | Turns a @FormId@ into a @FormRange@ by incrementing the base for the end Id
+unitRange :: FormId -> FormRange
+unitRange i = FormRange i $ add 1 i
 
--- ** Ways to evaluate a Form
+bracketState :: Monad m => FormState m a -> FormState m a
+bracketState k = do
+  FormRange startF1 _ <- get
+  res <- k
+  FormRange _ endF2 <- get
+  put $ FormRange startF1 endF2
+  pure res
 
+-- | Utility function: increment the current 'FormId'.
+incrementFormRange :: Monad m => FormState m ()
+incrementFormRange = do
+  FormRange _ endF1 <- get
+  put $ unitRange endF1
+
 -- | Run a form
-runForm
-  :: (Monad m)
-  => Environment m input
-  -> Text
+runForm :: Monad m 
+  => Text
   -> Form m input err view a
-  -> m (View err view, m (Result err (Proved a)))
-runForm env prefix' form =
-  evalStateT (runReaderT (unForm form) env) (unitRange (zeroId $ unpack prefix'))
+  -> m (View err view, Result err (Proved a))
+runForm prefix Form{formFormlet} =
+  evalStateT formFormlet (unitRange (FormId prefix (pure 0)))
 
--- | Run a form
-runForm'
-  :: (Monad m)
-  => Environment m input
-  -> Text
+-- | Run a form, and unwrap the result
+runForm_ :: (Monad m)
+  => Text
   -> Form m input err view a
-  -> m (view, Maybe a)
-runForm' env prefix form = do
-  (view', mresult) <- runForm env prefix form
-  result <- mresult
+  -> m (view , Maybe a)
+runForm_ prefix form = do 
+  (view', result) <- runForm prefix form
   pure $ case result of
-    Error e -> (unView view' e, Nothing)
+    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)
-  => Text -- ^ form prefix
-  -> Form m input err view a -- ^ form to view
-  -> m view
-viewForm prefix form = do
-  (v, _) <- runForm NoEnvironment prefix form
-  pure (unView v [])
-
 -- | Evaluate a form
 --
 -- Returns:
@@ -298,104 +338,173 @@
 --
 -- [@Right a@] on success.
 --
-eitherForm
-  :: (Monad m)
-  => Environment m input -- ^ Input environment
-  -> Text -- ^ Identifier for the form
+eitherForm :: (Monad m)
+  => Text -- ^ Identifier for the form
   -> Form m input err view a -- ^ Form to run
   -> m (Either view a) -- ^ Result
-eitherForm env id' form = do
-  (view', mresult) <- runForm env id' form
-  result <- mresult
-  pure $ case result of
+eitherForm id' form = do
+  (view', result) <- runForm id' form
+  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
+-- | infix mapView: succinctly mix the @view@ dsl and the formlets dsl  @foo \@$ do ..@
+infixr 0 @$
+(@$) :: Monad m => (view -> view') -> Form m input err view a -> Form m input err view' a
+(@$) = mapView
+
+-- | Utility Function: turn a view and pure value into a successful 'FormState'
+mkOk
   :: (Monad m)
-  => view -- ^ View to insert
-  -> Form m input err view () -- ^ Resulting form
-view view' = Form $ do
-  i <- getFormId
-  pure
-    ( View (const view')
-    , pure
-      ( Ok
-        ( Proved
-          { pos = FormRange i i
-          , unProved = ()
+  => FormId
+  -> view
+  -> a
+  -> FormState m (View err view, Result err (Proved a))
+mkOk i view' val = pure
+  ( View $ const $ view'
+  , Ok ( Proved
+      { pos = unitRange i
+      , unProved = val
+      } )
+  )
+
+-- | Lift the errors into the result type. This will cause the form to always 'succeed'
+formEither :: Monad m
+  => Form m input err view a 
+  -> Form m input err view (Either [err] a)
+formEither Form{formDecodeInput, formInitialValue, formFormlet} = Form
+  (\input -> do 
+    res <- formDecodeInput input
+    case res of
+      Left err -> pure $ Right $ Left [err]
+      Right x -> pure $ Right $ Right x
+  ) 
+  (fmap Right formInitialValue) 
+  ( do
+    range <- get
+    (view', res') <- formFormlet
+    let res = case res' of
+          Error err -> Left (map snd err)
+          Ok (Proved _ x) -> Right x
+    pure  
+      ( view'
+      , Ok $ Proved 
+          { pos = range
+          , unProved = res
           }
-        )
       )
-    )
+  )
 
--- | Append a unit form to the left. This is useful for adding labels or err
--- 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, Semigroup view)
-  => Form m input err view z
-  -> Form m input err view a
+-- | Utility function: Get the current input
+getFormInput :: Environment m input => FormState m (Value input)
+getFormInput = getFormId >>= getFormInput'
+
+-- | Utility function: Gets the input of an arbitrary 'FormId'.
+getFormInput' :: Environment m input => FormId -> FormState m (Value input)
+getFormInput' fid = lift $ environment fid
+
+-- | 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)
+
+-- | Turn a @view@ into a @Form@
+view :: Monad m => view -> Form m input err view ()
+view html = Form (successDecode ()) (pure ()) $ do
+  i <- getFormId
+  pure  ( View (const html)
+        , Ok $ Proved
+            { pos = FormRange i i
+            , unProved = ()
+            }
+        )
+
+-- | Change the underlying Monad of the form, usually a @lift@ or newtype
+mapFormMonad :: (Monad f)
+  => (forall x. m x -> f x)
   -> Form m input err view 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
-  pure (v1 <> v2, r)
+  -> Form f input err view a
+mapFormMonad f Form{formDecodeInput, formInitialValue, formFormlet} = Form 
+  { formDecodeInput = f . formDecodeInput
+  , formInitialValue = f formInitialValue
+  , formFormlet = do
+      (view', res) <- fstate formFormlet
+      pure $ (view', res)
+  }
+  where
+  fstate st = StateT $ f . runStateT st
 
-infixl 6 ++>
+-- | Catch errors purely
+catchFormError :: (Monad m)
+  => ([err] -> a)
+  -> Form m input err view a
+  -> Form m input err view a
+catchFormError ferr Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
+  i <- getFormId
+  (View viewf, res) <- formFormlet
+  case res of
+    Ok _ -> formFormlet
+    Error err -> mkOk i (viewf []) (ferr $ fmap snd err)
 
--- | Append a unit form to the right. See '++>'.
-(<++)
-  :: (Monad m, Semigroup view)
+-- | Catch errors inside @Form@ / @m@
+catchFormErrorM :: (Monad m)
   => Form m input err view a
-  -> Form m input err view z
+  -> ([err] -> Form m input err view a)
   -> Form m input err view 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
-  pure (v1 <> v2, r)
+catchFormErrorM form@(Form{formDecodeInput, formInitialValue}) e = Form formDecodeInput formInitialValue $ do
+  (_, res0) <- formFormlet form
+  case res0 of
+    Ok _ -> formFormlet form
+    Error err -> formFormlet $ e $ map snd err
 
-infixr 5 <++
+-- | Map over the @Result@ and @View@ of a form
+mapResult :: (Monad m)
+  => (Result err (Proved a) -> Result err (Proved a))
+  -> (View err view -> View err view)
+  -> Form m input err view a
+  -> Form m input err view a
+mapResult fres fview Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
+  (view', res) <- formFormlet
+  pure (fview view', fres res)
 
--- | 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
-  :: (Functor m)
-  => (view -> view') -- ^ Manipulator
-  -> Form m input err view a -- ^ Initial form
-  -> Form m input err view' a -- ^ Resulting form
-mapView f = Form . fmap (first $ fmap f) . unForm
+-- | Run the form with no environment, return only the html.
+-- This means that the values will always be their defaults
+viewForm :: (Monad m)
+  => Text -- ^ form prefix
+  -> Form m input err view a -- ^ form to view
+  -> m view
+viewForm prefix form = do
+  (v, _) <- getNoEnvironment $ runForm prefix $ mapFormMonad NoEnvironment form
+  pure (unView v [])
 
--- | infix mapView: succinct `foo @$ do ..`
-infixr 0 @$
-(@$) :: Monad m => (view -> view) -> Form m input err view a -> Form m input err view a
-(@$) = mapView
+-- | lift the result of a decoding to a @Form@
+pureRes :: (Monad m, Monoid view, FormError input err)
+  => a
+  -> Either err a
+  -> Form m input err view a
+pureRes def x' = case x' of
+  Right x -> Form (successDecode x) (pure x) $ do
+    i <- getFormId
+    pure  ( mempty
+          , Ok $ Proved
+              { pos = FormRange i i
+              , unProved = x
+              }
+          )
+  Left e -> Form (successDecode def) (pure def) $ do
+    i <- getFormId
+    pure ( mempty
+         , Error [(FormRange i i, e)]
+         )
 
--- | Utility Function: turn a view and pure value into a successful 'FormState'
-mkOk
-  :: (Monad m)
-  => FormId
-  -> view
-  -> a
-  -> FormState m input (View err view, m (Result err (Proved a)))
-mkOk i view' val =
-  pure
-    ( View $ const $ view'
-    , pure $
-      Ok
-        ( Proved
-          { pos = unitRange i
-          , unProved = val
-          }
-        )
-    )
+-- | @Form@ is a @MonadTrans@, but we can't have an instance of it because of the order and kind of its type variables
+liftForm :: (Monad m, Monoid view) => m a -> Form m input err view a
+liftForm x = Form (const (fmap Right x)) x $ do
+  res <- lift x
+  i <- getFormId
+  pure (mempty, Ok $ Proved (FormRange i i) res)
+
diff --git a/src/Ditto/Generalized.hs b/src/Ditto/Generalized.hs
deleted file mode 100644
--- a/src/Ditto/Generalized.hs
+++ /dev/null
@@ -1,108 +0,0 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeFamilies #-}
-
--- This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
-
-module Ditto.Generalized where
-
-import Ditto.Backend
-import Ditto.Core
-import Ditto.Result
-import qualified Ditto.Generalized.Internal as G
-
--- | used for constructing elements like @\<input type=\"text\"\>@, which pure a single input value.
-input :: (Monad m, FormError input err) => (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view a
-input = G.input getFormId
-
--- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data.
-inputMaybe
-  :: (Monad m, FormError input err)
-  => (input -> Either err a)
-  -> (FormId -> Maybe a -> view)
-  -> Maybe a
-  -> Form m input err view (Maybe a)
-inputMaybe = G.inputMaybe getFormId
-
--- | used to construct elements with optional initial values, which are still required
-inputMaybeReq
-  :: (Monad m, FormError input err)
-  => (input -> Either err a)
-  -> (FormId -> Maybe a -> view)
-  -> Maybe a
-  -> Form m input err view a
-inputMaybeReq = G.inputMaybeReq getFormId
-
--- | used for elements like @\<input type=\"reset\"\>@ which take a value, but are never present in the form data set.
-inputNoData
-  :: (Monad m)
-  => (FormId -> view)
-  -> Form m input err view ()
-inputNoData = G.inputNoData getFormId
-
--- | used for @\<input type=\"file\"\>@
-inputFile
-  :: forall m input err view. (Monad m, FormInput input, FormError input err)
-  => (FormId -> view)
-  -> Form m input err view (FileType input)
-inputFile = G.inputFile getFormId
-
--- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes
-inputMulti
-  :: forall m input err view a lbl. (FormError input err, 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 err view [a]
-inputMulti = G.inputMulti getFormId
-
--- | radio buttons, single @\<select\>@ boxes
-inputChoice
-  :: forall a m err input lbl view. (FormError input err, 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 err view a
-inputChoice = G.inputChoice getFormId
-
--- | radio buttons, single @\<select\>@ boxes
-inputChoiceForms
-  :: forall a m err input lbl view. (Monad m, FormError input err, FormInput input)
-  => a
-  -> [(Form m input err view a, lbl)] -- ^ value, label
-  -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -- ^ function which generates the view
-  -> Form m input err view a
-inputChoiceForms = G.inputChoiceForms getFormId
-
--- | used to create @\<label\>@ elements
-label
-  :: Monad m
-  => (FormId -> view)
-  -> Form m input err view ()
-label = G.label getFormId
-
--- | used to add a list of err 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
-  => ([err] -> view) -- ^ function to convert the err messages into a view
-  -> Form m input err view ()
-errors = G.errors
-
--- | similar to 'errors' but includes err messages from children of the form as well.
-childErrors
-  :: Monad m
-  => ([err] -> view)
-  -> Form m input err view ()
-childErrors = G.childErrors
-
--- | modify the view of a form based on its errors
-withErrors
-  :: Monad m
-  => (view -> [err] -> view)
-  -> Form m input err view a
-  -> Form m input err view a
-withErrors = G.withErrors
-
diff --git a/src/Ditto/Generalized/Internal.hs b/src/Ditto/Generalized/Internal.hs
--- a/src/Ditto/Generalized/Internal.hs
+++ b/src/Ditto/Generalized/Internal.hs
@@ -1,214 +1,204 @@
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE DoAndIfThenElse #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE 
+    NamedFieldPuns
+  , OverloadedStrings
+  , ScopedTypeVariables
+  , LambdaCase
+  , TypeFamilies
+#-}
 
--- This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
+-- | This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
 
 module Ditto.Generalized.Internal where
 
-import Control.Applicative ((<$>))
-import Control.Monad (foldM)
+import Control.Monad.State.Class (get)
 import Control.Monad.Trans (lift)
-import Data.Bifunctor
-import Numeric (readDec)
+import Data.List (find)
+import Data.List.NonEmpty (NonEmpty(..))
+import Data.Traversable (for)
 import Ditto.Backend
 import Ditto.Core
-import Ditto.Result
-import qualified Data.IntSet as IS
+import Ditto.Types
 
 -- | used for constructing elements like @\<input type=\"text\"\>@, which pure a single input value.
-input
-  :: forall m input err a view. (Monad m, FormError input err)
-  => FormState m input FormId
+input :: forall m input err a view. (Environment m input, FormError input err)
+  => FormState m FormId
   -> (input -> Either err a)
   -> (FormId -> a -> view)
   -> a
   -> Form m input err view a
-input i' fromInput toView initialValue =
-  Form $ do
-    i <- i'
+input formSId fromInput toView initialValue =
+  Form (pure . fromInput) (pure initialValue) $ do
+    i <- formSId
     v <- getFormInput' i
     case v of
-      Default ->
-        pure
-          ( View $ const $ toView i initialValue
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = initialValue
-                }
-              )
-          )
-      Found x -> case fromInput x of
+      Default -> pure
+        ( View $ const $ toView i initialValue
+        , Ok $ Proved
+            { pos = unitRange i
+            , unProved = initialValue
+            }
+        )
+      Found inp -> case fromInput inp of
         Right a -> pure
           ( View $ const $ toView i a
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = a
-                }
-              )
+          , Ok $ Proved
+              { pos = unitRange i
+              , unProved = a
+              }
           )
         Left err -> pure
           ( View $ const $ toView i initialValue
-          , pure $ Error [(unitRange i, err)]
+          , Error [(unitRange i, err)]
           )
       Missing -> pure
         ( View $ const $ toView i initialValue
-        , pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
+        , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
         )
 
-inputMaybeReq
-  :: forall m input err a view. (Monad m, FormError input err)
-  => FormState m input FormId
-  -> (input -> Either err a)
-  -> (FormId -> Maybe a -> view)
-  -> Maybe a
-  -> Form m input err view a
-inputMaybeReq i' fromInput toView initialValue =
-  Form $ do
-    i <- i'
+-- | this is necessary in order to basically map over the decoding function
+inputList :: forall m input err a view view'. (Monad m, FormError input err, Environment m input)
+  => FormState m FormId
+  -> (input -> m (Either err [a])) -- ^ decoding function for the list
+  -> ([view] -> view') -- ^ how to concatenate views
+  -> [a] -- ^ initial values
+  -> view' -- ^ view to generate in the fail case
+  -> (a -> Form m input err view a)
+  -> Form m input err view' [a]
+inputList formSId fromInput viewCat initialValue defView createForm =
+  Form fromInput (pure initialValue) $ do
+    i <- formSId
     v <- getFormInput' i
     case v of
-      Default ->
+      Default -> do
+        let ivs' = case initialValue of
+              [] -> []
+              _ -> initialValue
+        views <- for ivs' $ \x -> do
+          (View viewF, _) <- formFormlet $ createForm x 
+          pure $ viewF []
         pure
-          ( View $ const $ toView i initialValue
-          , case initialValue of 
-              Just x -> pure $
-                Ok ( Proved
-                       { pos = unitRange i
-                       , unProved = x
-                       }
-                   )
-              Nothing -> pure $ Error [(unitRange i, commonFormError (MissingDefaultValue :: CommonFormError input) :: err)]
+          ( View $ const $ viewCat views
+          , Ok $ Proved
+              { pos = unitRange i
+              , unProved = ivs'
+              }
           )
-      Found x -> case fromInput x of
-        Right a -> pure
-          ( View $ const $ toView i (Just a)
-          , pure $
-            Ok
-              ( Proved
+      Found inp -> lift (fromInput inp) >>= \case
+        Right xs -> do
+          views <- for xs $ \x -> do
+            (View viewF, _) <- formFormlet $ createForm x 
+            pure $ viewF []
+          pure
+            ( View $ const $ viewCat views
+            , Ok $ Proved
                 { pos = unitRange i
-                , unProved = a
+                , unProved = xs
                 }
-              )
-          )
-        Left err -> pure
-          ( View $ const $ toView i initialValue
-          , pure $ Error [(unitRange i, err)]
+            )
+        Left err -> do
+          let err' = [(unitRange i, err)]
+          views <- for initialValue $ \x -> do
+            (View viewF, _) <- formFormlet $ createForm x 
+            pure $ viewF err'
+          pure
+            ( View $ const $ viewCat views
+            , Error err'
+            )
+      Missing -> do 
+        pure
+          ( View $ const defView
+          , Ok $ Proved
+              { pos = unitRange i
+              , unProved = []
+              }
           )
-      Missing -> pure
-        ( View $ const $ toView i initialValue
-        , pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
-        )
 
-
 -- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data.
-inputMaybe
-  :: (Monad m, FormError input err)
-  => FormState m input FormId
+inputMaybe :: (Monad m, FormError input err, Environment m input)
+  => FormState m FormId
   -> (input -> Either err a)
   -> (FormId -> Maybe a -> view)
   -> Maybe a
   -> Form m input err view (Maybe a)
 inputMaybe i' fromInput toView initialValue =
-  Form $ do
+  Form (pure . fmap Just . fromInput) (pure initialValue) $ do
     i <- i'
     v <- getFormInput' i
     case v of
       Default -> pure
           ( View $ const $ toView i initialValue
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = initialValue
-                }
-              )
+          , Ok ( Proved
+              { pos = unitRange i
+              , unProved = initialValue
+              })
           )
       Found x -> case fromInput x of
         Right a -> pure
           ( View $ const $ toView i (Just a)
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = (Just a)
-                }
-              )
+          , Ok $ Proved
+              { pos = unitRange i
+              , unProved = (Just a)
+              }
           )
         Left err -> pure
           ( View $ const $ toView i initialValue
-          , pure $ Error [(unitRange i, err)]
+          , Error [(unitRange i, err)]
           )
       Missing -> pure
-          ( View $ const $ toView i initialValue
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = Nothing
-                }
-              )
-          )
+        ( View $ const $ toView i initialValue
+        , Ok $ Proved
+            { 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)
-  => FormState m input FormId
+inputNoData :: (Monad m)
+  => FormState m FormId
   -> (FormId -> view)
   -> Form m input err view ()
 inputNoData i' toView =
-  Form $ do
+  Form (successDecode ()) (pure ()) $ do
     i <- i'
     pure
       ( View $ const $ toView i
-      , pure $
-        Ok
-          ( Proved
-            { pos = unitRange i
-            , unProved = ()
-            }
-          )
+      , Ok ( Proved
+          { pos = unitRange i
+          , unProved = ()
+          })
       )
 
 -- | used for @\<input type=\"file\"\>@
-inputFile
-  :: forall m input err view. (Monad m, FormInput input, FormError input err)
-  => FormState m input FormId
+inputFile :: forall m ft input err view. (Monad m, FormInput input, FormError input err, Environment m input, ft ~ FileType input, Monoid ft)
+  => FormState m FormId
   -> (FormId -> view)
   -> Form m input err view (FileType input)
 inputFile i' toView =
-  Form $ do
+  Form (pure . getInputFile') (pure mempty) $ do -- FIXME
     i <- i'
     v <- getFormInput' i
     case v of
       Default ->
         pure
           ( View $ const $ toView i
-          , pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
+          , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
           )
       Found x -> case getInputFile' x of
         Right a -> pure
           ( View $ const $ toView i
-          , pure $
-            Ok
-              ( Proved
-                { pos = unitRange i
-                , unProved = a
-                }
-              )
+          , Ok ( Proved
+              { pos = unitRange i
+              , unProved = a
+              })
           )
         Left err -> pure
           ( View $ const $ toView i
-          , pure $ Error [(unitRange i, err)]
+          , Error [(unitRange i, err)]
           )
       Missing ->
         pure
           ( View $ const $ toView i
-          , pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) ::err)]
+          , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) ::err)]
           )
   where
     -- just here for the type-signature to make the type-checker happy
@@ -216,119 +206,126 @@
     getInputFile' = getInputFile
 
 -- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes
-inputMulti
-  :: forall m input err view a lbl. (FormError input err, FormInput input, Monad m)
-  => FormState m input FormId
+inputMulti :: forall m input err view a lbl. (FormError input err, FormInput input, Environment m input, Eq a)
+  => FormState m FormId
   -> [(a, lbl)] -- ^ value, label, initially checked
-  -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view
+  -> (input -> Either err [a])
+  -> (FormId -> [Choice lbl a] -> view) -- ^ function which generates the view
   -> (a -> Bool) -- ^ isChecked/isSelected initially
   -> Form m input err view [a]
-inputMulti i' choices mkView isSelected =
-  Form $ do
+inputMulti i' choices fromInput mkView isSelected =
+  Form (pure . fromInput) (pure $ map fst choices) $ do
     i <- i'
     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 ->
+      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 -> do
         -- just means that no checkboxes were checked
-        do
-          view' <- mkView i <$> augmentChoices (map (\(x, y) -> (x, y, False)) choices)
-          mkOk i view' []
+        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 pure an internal err?
-            keys = IS.fromList $ map readDec' $ getInputStrings v
+        let keys = either (const []) id $ fromInput v
             (choices', vals) =
               foldr
-                ( \(i0, (a, lbl)) (c, v0) ->
-                  if IS.member i0 keys
+                ( \(a, lbl) (c, v0) ->
+                  if a `elem` keys
                   then ((a, lbl, True) : c, a : v0)
                   else ((a, lbl, False) : c, v0)
                 )
                 ([], []) $
-                zip [0..] choices
+                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, (_, lbl, checked)) =
-      do
-        incFormId
-        i <- i'
-        pure (i, vl, lbl, checked)
+    augmentChoices :: (Monad m) => [(a, lbl, Bool)] -> FormState m [Choice lbl a]
+    augmentChoices choices' = mapM augmentChoice choices'
+    augmentChoice :: (Monad m) => (a, lbl, Bool) -> FormState m (Choice lbl a)
+    augmentChoice (a, lbl, selected) = do
+      i <- i'
+      pure $ Choice i lbl selected a
 
+-- | a choice for inputChoice
+data Choice lbl a = Choice
+  { choiceFormId :: FormId -- ^ the formId
+  , choiceLabel :: lbl -- ^ <label>
+  , choiceIsSelected :: Bool -- ^ is the choice selected
+  , choiceVal :: a -- ^ the haskell value of the choice
+  }
+
 -- | radio buttons, single @\<select\>@ boxes
-inputChoice
-  :: forall a m err input lbl view. (FormError input err, FormInput input, Monad m)
-  => FormState m input FormId
+inputChoice :: forall a m err input lbl view. (FormError input err, FormInput input, Monad m, Eq a, Monoid view, Environment m input)
+  => FormState m FormId
   -> (a -> Bool) -- ^ is default
-  -> [(a, lbl)] -- ^ value, label
-  -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view
+  -> NonEmpty (a, lbl) -- ^ value, label
+  -> (input -> Either err a)
+  -> (FormId -> [Choice lbl a] -> view) -- ^ function which generates the view
   -> Form m input err view a
-inputChoice i' isDefault choices mkView =
-  Form $ do
+inputChoice i' isDefault choices@(headChoice :| _) fromInput mkView = do
+  let f = case find isDefault (fmap fst choices) of
+        Nothing -> Form (pure . fromInput) (pure $ fst headChoice)
+        Just defChoice -> Form (pure . fromInput) (pure defChoice)
+  f $ do
     i <- i'
     inp <- getFormInput' i
     case inp of
-      Default ->
-        do
-          let (choices', def) = markSelected choices
-          view' <- mkView i <$> augmentChoices choices'
-          mkOk' i view' def
-      Missing ->
+      Default -> do
+        let (choices', def) = markSelected choices
+        view' <- mkView i <$> augmentChoices choices'
+        mkOk' i view' def
+      Missing -> do
         -- 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' :: String -> Int
-              readDec' str' = case readDec str' of
-                [(n, [])] -> n
-                _ -> (-1) -- FIXME: should probably pure an internal err?
-              estr = getInputString v :: Either err String
-              key = second readDec' estr
-              (choices', mval) =
-                foldr
-                  ( \(i0, (a, lbl)) (c, v0) ->
-                    if either (const False) (==i0) key
-                    then ((a, lbl, True) : c, Just a)
-                    else ((a, lbl, False) : c, v0)
-                  )
-                  ([], Nothing) $
-                  zip [0..] choices
-          view' <- mkView i <$> augmentChoices choices'
-          case mval of
-            Nothing ->
-              pure
+        let (choices', def) = markSelected choices
+        view' <- mkView i <$> augmentChoices choices'
+        mkOk' i view' def
+      Found v -> do
+        case fromInput v of
+          Left err -> do
+            let choices' =
+                  foldr
+                    ( \(a, lbl) c -> (a, lbl, False) : c )
+                    []
+                    choices
+            view' <- mkView i <$> augmentChoices choices'
+            pure
+              ( View $ const view'
+              , Error [(unitRange i, err)]
+              )
+          Right key -> do
+            let (choices', mval) =
+                  foldr
+                    ( \(a, lbl) (c, v0) ->
+                      if key == a
+                      then ((a, lbl, True) : c, Just a)
+                      else ((a, lbl, False) : c, v0)
+                    )
+                    ([], Nothing) $
+                    choices
+            view' <- mkView i <$> augmentChoices choices'
+            case mval of
+              Nothing -> pure
                 ( View $ const view'
-                , pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
+                , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
                 )
-            (Just val) -> mkOk i view' val
+              Just val -> mkOk i view' val
   where
     mkOk' i view' (Just val) = mkOk i view' val
     mkOk' i view' Nothing =
       pure
         ( View $ const $ view'
-        , pure $ Error [(unitRange i, commonFormError (MissingDefaultValue :: CommonFormError input) :: err)]
+        , Error [(unitRange i, commonFormError (MissingDefaultValue :: CommonFormError input) :: err)]
         )
-    markSelected :: [(a, lbl)] -> ([(a, lbl, Bool)], Maybe a)
+    markSelected :: Foldable f => f (a, lbl) -> ([(a, lbl, Bool)], Maybe a)
     markSelected cs =
       foldr
         ( \(a, lbl) (vs, ma) ->
@@ -338,197 +335,89 @@
         )
         ([], 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
-        incFormId
-        i <- i'
-        pure (i, vl, lbl, selected)
-
--- | radio buttons, single @\<select\>@ boxes
-inputChoiceForms
-  :: forall a m err input lbl view. (Monad m, FormError input err, FormInput input)
-  => FormState m input FormId
-  -> a
-  -> [(Form m input err view a, lbl)] -- ^ value, label
-  -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -- ^ function which generates the view
-  -> Form m input err view a
-inputChoiceForms i' def choices mkView =
-  Form $ do
-    i <- i' -- id used for the 'name' attribute of the radio buttons
-    inp <- getFormInput' i
-    case inp of
-      Default ->
-        -- produce view for GET request
-        do
-          choices' <- mapM viewSubForm =<< augmentChoices (selectFirst choices)
-          let view' = mkView i choices'
-          mkOk' i view' def
-      Missing ->
-        -- shouldn't ever happen...
-        do
-          choices' <- mapM viewSubForm =<< augmentChoices (selectFirst choices)
-          let view' = mkView i choices'
-          mkOk' i view' def
-      (Found v) ->
-        do
-          let readDec' str' = case readDec str' of
-                [(n, [])] -> n
-                _ -> (-1) -- FIXME: should probably pure an internal err?
-              estr = getInputString v :: Either err String
-              key = second readDec' estr
-          choices' <- augmentChoices $ markSelected key (zip [0..] choices)
-          (choices'', mres) <-
-            foldM
-              ( \(views, res) (fid, val, iview, frm, lbl, selected) -> do
-                incFormId
-                if selected
-                then do
-                    (v0, mres) <- unForm frm
-                    res' <- lift $ lift mres
-                    case res' of
-                      Ok{} -> do
-                        pure (((fid, val, iview, unView v0 [], lbl, selected) : views), pure res')
-                      Error errs -> do
-                        pure (((fid, val, iview, unView v0 errs, lbl, selected) : views), pure res')
-                else do
-                    (v0, _) <- unForm frm
-                    pure ((fid, val, iview, unView v0 [], lbl, selected) : views, res)
-              )
-              ([], pure $ Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)])
-              (choices')
-          let view' = mkView i (reverse choices'')
-          pure (View (const view'), mres)
-  where
-    -- | Utility Function: turn a view and pure value into a successful 'FormState'
-    mkOk'
-      :: (Monad m)
-      => FormId
-      -> view
-      -> a
-      -> FormState m input (View err view, m (Result err (Proved a)))
-    mkOk' _ view' _ =
-      pure
-        ( View $ const view'
-        , pure $ Error []
-        )
-    selectFirst :: [(Form m input err view a, lbl)] -> [(Form m input err view a, lbl, Bool)]
-    selectFirst ((frm, lbl) : fs) = (frm, lbl, True) : map (\(frm', lbl') -> (frm', lbl', False)) fs
-    selectFirst [] = []
-    markSelected :: Either e Int -> [(Int, (Form m input err view a, lbl))] -> [(Form m input err view a, lbl, Bool)]
-    markSelected en choices' =
-      map (\(i, (f, lbl)) -> (f, lbl, either (const False) (==i) en)) choices'
-    viewSubForm :: (FormId, Int, FormId, Form m input err view a, lbl, Bool) -> FormState m input (FormId, Int, FormId, view, lbl, Bool)
-    viewSubForm (fid, vl, iview, frm, lbl, selected) =
-      do
-        incFormId
-        (v, _) <- unForm frm
-        pure (fid, vl, iview, unView v [], lbl, selected)
-    augmentChoices :: (Monad m) => [(Form m input err view a, lbl, Bool)] -> FormState m input [(FormId, Int, FormId, Form m input err view a, lbl, Bool)]
-    augmentChoices choices' = mapM augmentChoice (zip [0..] choices')
-    augmentChoice :: (Monad m) => (Int, (Form m input err view a, lbl, Bool)) -> FormState m input (FormId, Int, FormId, Form m input err view a, lbl, Bool)
-    augmentChoice (vl, (frm, lbl, selected)) =
-      do
-        incFormId
-        i <- i'
-        incFormId
-        iview <- getFormId
-        pure (i, vl, iview, frm, lbl, selected)
-
-{-
-              case inp of
-                (Found v) ->
-                    do let readDec' str = case readDec str of
-                                            [(n,[])] -> n
-                                            _ -> (-1) -- FIXME: should probably pure an internal err?
-                           (Right str) = getInputString v :: Either err 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
-
+    augmentChoices :: (Monad m) => [(a, lbl, Bool)] -> FormState m [Choice lbl a]
+    augmentChoices choices' = mapM augmentChoice choices'
+    augmentChoice :: (Monad m) => (a, lbl, Bool) -> FormState m (Choice lbl a)
+    augmentChoice (a, lbl, selected) = do
+      i <- i'
+      pure $ Choice i lbl selected a
 
--}
 -- | used to create @\<label\>@ elements
-label
-  :: Monad m
-  => FormState m input FormId
+label :: Monad m
+  => FormState m FormId
   -> (FormId -> view)
   -> Form m input err view ()
-label i' f =
-  Form $ do
-    id' <- i'
-    pure
-      ( View (const $ f id')
-      , pure
-        ( Ok $ Proved
-          { pos = unitRange id'
-          , unProved = ()
-          }
-        )
+label i' f = Form (successDecode ()) (pure ()) $ do
+  id' <- i'
+  pure
+    ( View (const $ f id')
+    , ( Ok $ Proved
+        { pos = unitRange id'
+        , unProved = ()
+        }
       )
+    )
 
 -- | used to add a list of err 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
+-- '<*' or '*>'.
+errors :: Monad m
   => ([err] -> view) -- ^ function to convert the err messages into a view
   -> Form m input err view ()
-errors f =
-  Form $ do
-    range <- getFormRange
-    pure
-      ( View (f . retainErrors range)
-      , pure
-        ( Ok $ Proved
-          { pos = range
-          , unProved = ()
-          }
-        )
+errors f = Form (successDecode ()) (pure ()) $ do
+  range <- get
+  pure
+    ( View (f . retainErrors range)
+    , ( Ok $ Proved
+        { pos = range
+        , unProved = ()
+        }
       )
+    )
 
 -- | similar to 'errors' but includes err messages from children of the form as well.
-childErrors
-  :: Monad m
+childErrors :: Monad m
   => ([err] -> view)
   -> Form m input err view ()
-childErrors f =
-  Form $ do
-    range <- getFormRange
-    pure
-      ( View (f . retainChildErrors range)
-      , pure
-        ( Ok $ Proved
-          { pos = range
-          , unProved = ()
-          }
-        )
+childErrors f = Form (successDecode ()) (pure ()) $ do
+  range <- get
+  pure
+    ( View (f . retainChildErrors range)
+    , ( Ok $ Proved
+        { pos = range
+        , unProved = ()
+        }
       )
+    )
 
--- | modify the view of a form based on its errors
-withErrors
-  :: Monad m
+-- | modify the view of a form based on its child errors
+withChildErrors :: Monad m
   => (view -> [err] -> view)
   -> Form m input err view a
   -> Form m input err view a
-withErrors f form = Form $ do
-  (View v, r) <- unForm form
-  range <- getFormRange
+withChildErrors f Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
+  (View v, r) <- formFormlet
+  range <- get
   pure
-    ( View
-        (\x ->
-          let errs = retainChildErrors range x
-          in f (v x) errs
-        )
+    ( View $ \x ->
+        let errs = retainChildErrors range x
+        in f (v x) errs
     , r
     )
 
+-- | modify the view of a form based on its errors
+withErrors :: Monad m
+  => (view -> [err] -> view)
+  -> Form m input err view a
+  -> Form m input err view a
+withErrors f Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
+  (View v, r) <- formFormlet
+  range <- get
+  pure
+    ( View $ \x ->
+        let errs = retainErrors range x
+        in f (v x) errs
+    , r
+    )
diff --git a/src/Ditto/Generalized/Named.hs b/src/Ditto/Generalized/Named.hs
--- a/src/Ditto/Generalized/Named.hs
+++ b/src/Ditto/Generalized/Named.hs
@@ -1,35 +1,49 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE DoAndIfThenElse #-}
+{-# LANGUAGE 
+    ScopedTypeVariables
+  , TypeFamilies
+#-}
 
--- This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
+-- | This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
+--
+-- For unnamed (enumerated) formlets, see @Ditto.Generalized.Unnamed@
 
-module Ditto.Generalized.Named where
+module Ditto.Generalized.Named
+  ( G.Choice(..)
+  , input
+  , inputMaybe
+  , inputNoData
+  , inputFile
+  , inputMulti
+  , inputChoice
+  , inputList
+  , label
+  , errors
+  , childErrors
+  , withErrors
+  , G.withChildErrors
+  , ireq
+  , iopt
+  ) where
 
 import Ditto.Backend
 import Ditto.Core
-import Ditto.Result
+import Ditto.Types
+import Data.Text (Text)
+import Data.List.NonEmpty (NonEmpty(..))
 import qualified Ditto.Generalized.Internal as G
 
 -- | used for constructing elements like @\<input type=\"text\"\>@, which pure a single input value.
-input :: (Monad m, FormError input err) => String -> (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view a
-input name = G.input (getNamedFormId name)
-
-
--- | used to construct elements with optional initial values, which are still required
-inputMaybeReq
-  :: (Monad m, FormError input err)
-  => String
-  -> (input -> Either err a)
-  -> (FormId -> Maybe a -> view)
-  -> Maybe a
+input :: (Environment m input, FormError input err) 
+  => Text 
+  -> (input -> Either err a) 
+  -> (FormId -> a -> view) 
+  -> a 
   -> Form m input err view a
-inputMaybeReq name = G.inputMaybeReq (getNamedFormId name)
+input name = G.input (getNamedFormId name)
 
 -- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data.
-inputMaybe
-  :: (Monad m, FormError input err)
-  => String
+inputMaybe :: (Environment m input, FormError input err)
+  => Text
   -> (input -> Either err a)
   -> (FormId -> Maybe a -> view)
   -> Maybe a
@@ -37,55 +51,53 @@
 inputMaybe name = G.inputMaybe (getNamedFormId name)
 
 -- | used for elements like @\<input type=\"reset\"\>@ which take a value, but are never present in the form data set.
-inputNoData
-  :: (Monad m)
-  => String
+inputNoData :: (Environment m input)
+  => Text
   -> (FormId -> view)
   -> Form m input err view ()
 inputNoData name = G.inputNoData (getNamedFormId name)
 
 -- | used for @\<input type=\"file\"\>@
-inputFile
-  :: forall m input err view. (Monad m, FormInput input, FormError input err)
-  => String
+inputFile :: forall m input err view ft. (Environment m input, FormInput input, FormError input err, ft ~ FileType input, Monoid ft)
+  => Text
   -> (FormId -> view)
   -> Form m input err view (FileType input)
 inputFile name = G.inputFile (getNamedFormId name)
 
 -- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes
-inputMulti
-  :: forall m input err view a lbl. (FormError input err, FormInput input, Monad m)
-  => String
+inputMulti :: forall m input err view a lbl. (FormError input err, FormInput input, Environment m input, Eq a)
+  => Text
   -> [(a, lbl)] -- ^ value, label, initially checked
-  -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view
+  -> (input -> Either err [a])
+  -> (FormId -> [G.Choice lbl a] -> view) -- ^ function which generates the view
   -> (a -> Bool) -- ^ isChecked/isSelected initially
   -> Form m input err view [a]
 inputMulti name = G.inputMulti (getNamedFormId name)
 
 -- | radio buttons, single @\<select\>@ boxes
-inputChoice
-  :: forall a m err input lbl view. (FormError input err, FormInput input, Monad m)
-  => String
+inputChoice :: forall a m err input lbl view. (FormError input err, FormInput input, Environment m input, Eq a, Monoid view)
+  => Text
   -> (a -> Bool) -- ^ is default
-  -> [(a, lbl)] -- ^ value, label
-  -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view
+  -> NonEmpty (a, lbl) -- ^ value, label
+  -> (input -> Either err a)
+  -> (FormId -> [G.Choice lbl a] -> view) -- ^ function which generates the view
   -> Form m input err view a
 inputChoice name = G.inputChoice (getNamedFormId name)
 
--- | radio buttons, single @\<select\>@ boxes
-inputChoiceForms
-  :: forall a m err input lbl view. (Monad m, FormError input err, FormInput input)
-  => String
-  -> a
-  -> [(Form m input err view a, lbl)] -- ^ value, label
-  -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -- ^ function which generates the view
-  -> Form m input err view a
-inputChoiceForms name = G.inputChoiceForms (getNamedFormId name)
+-- | this is necessary in order to basically map over the decoding function
+inputList :: forall m input err a view. (Monad m, FormError input err, Environment m input)
+  => Text
+  -> (input -> m (Either err [a])) -- ^ decoding function for the list
+  -> ([view] -> view) -- ^ how to concatenate views
+  -> [a] -- ^ initial values
+  -> view -- ^ view to generate in the fail case
+  -> (a -> Form m input err view a)
+  -> Form m input err view [a]
+inputList name = G.inputList (getNamedFormId name)
 
 -- | used to create @\<label\>@ elements
-label
-  :: Monad m
-  => String
+label :: Environment m input
+  => Text
   -> (FormId -> view)
   -> Form m input err view ()
 label name = G.label (getNamedFormId name)
@@ -94,25 +106,93 @@
 --
 -- 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
+-- '<*' or '*>'.
+errors :: Environment m input
   => ([err] -> view) -- ^ function to convert the err messages into a view
   -> Form m input err view ()
 errors = G.errors
 
 -- | similar to 'errors' but includes err messages from children of the form as well.
-childErrors
-  :: Monad m
+childErrors :: Environment m input
   => ([err] -> view)
   -> Form m input err view ()
 childErrors = G.childErrors
 
 -- | modify the view of a form based on its errors
-withErrors
-  :: Monad m
+withErrors :: Environment m input
   => (view -> [err] -> view)
   -> Form m input err view a
   -> Form m input err view a
 withErrors = G.withErrors
+
+-- | a @Form@ with no @view@
+ireq :: forall m input view err a. (Monoid view, Environment m input, FormError input err)
+  => Text 
+  -> (input -> Either err a)
+  -> a
+  -> Form m input err view a
+ireq name fromInput initialValue = Form (pure . fromInput) (pure initialValue) $ do
+  i <- getNamedFormId name
+  v <- getFormInput' i
+  case v of
+    Default -> pure
+      ( mempty
+      , Ok ( Proved
+          { pos = unitRange i
+          , unProved = initialValue
+          } )
+      )
+    Found inp -> case fromInput inp of
+      Right a -> pure
+        ( mempty
+        , Ok ( Proved
+            { pos = unitRange i
+            , unProved = a
+            } )
+        )
+      Left err -> pure
+        ( mempty
+        , Error [(unitRange i, err)]
+        )
+    Missing -> pure
+      ( mempty
+      , Error [(unitRange i, commonFormError (InputMissing i :: CommonFormError input) :: err)]
+      )
+
+-- | an optional @Form@ with no @view@
+iopt :: forall m input view err a. (Monoid view, Environment m input, FormError input err)
+  => Text 
+  -> (input -> Either err a)
+  -> Maybe a
+  -> Form m input err view (Maybe a)
+iopt name fromInput initialValue = Form (pure . fmap Just . fromInput) (pure initialValue) $ do
+  i <- getNamedFormId name
+  v <- getFormInput' i
+  case v of
+    Default -> pure
+      ( mempty
+      , Ok ( Proved
+          { pos = unitRange i
+          , unProved = initialValue
+          } )
+      )
+    Found inp -> case fromInput inp of
+      Right a -> pure
+        ( mempty
+        , Ok ( Proved
+            { pos = unitRange i
+            , unProved = Just a
+            } )
+        )
+      Left err -> pure
+        ( mempty
+        , Error [(unitRange i, err)]
+        )
+    Missing -> pure
+      ( mempty
+      , Ok ( Proved
+          { pos = unitRange i
+          , unProved = Nothing
+          } )
+      )
 
diff --git a/src/Ditto/Generalized/Unnamed.hs b/src/Ditto/Generalized/Unnamed.hs
new file mode 100644
--- /dev/null
+++ b/src/Ditto/Generalized/Unnamed.hs
@@ -0,0 +1,117 @@
+{-# LANGUAGE 
+    ScopedTypeVariables
+  , TypeFamilies
+#-}
+
+-- | This module provides helper functions for HTML input elements. These helper functions are not specific to any particular web framework or html library.
+--
+-- Additionally, the inputs generated with the functions from this module will have their names/ids automatically enumerated.
+--
+-- For named formlets, see @Ditto.Generalized.Named@
+module Ditto.Generalized.Unnamed
+  ( G.Choice(..)
+  , input
+  , inputMaybe
+  , inputNoData
+  , inputFile
+  , inputMulti
+  , inputChoice
+  , inputList
+  , label
+  , errors
+  , childErrors
+  , withErrors
+  , G.withChildErrors
+  ) where
+
+import Data.List.NonEmpty (NonEmpty(..))
+import Ditto.Backend
+import Ditto.Core
+import Ditto.Types
+import qualified Ditto.Generalized.Internal as G
+
+-- | used for constructing elements like @\<input type=\"text\"\>@, which pure a single input value.
+input :: (Environment m input, FormError input err) 
+  => (input -> Either err a) 
+  -> (FormId -> a -> view) 
+  -> a 
+  -> Form m input err view a
+input = G.input getFormId
+
+-- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data.
+inputMaybe :: (Environment m input, FormError input err)
+  => (input -> Either err a)
+  -> (FormId -> Maybe a -> view)
+  -> Maybe a
+  -> Form m input err view (Maybe a)
+inputMaybe = G.inputMaybe getFormId
+
+-- | used for elements like @\<input type=\"reset\"\>@ which take a value, but are never present in the form data set.
+inputNoData :: (Environment m input)
+  => (FormId -> view)
+  -> Form m input err view ()
+inputNoData = G.inputNoData getFormId
+
+-- | used for @\<input type=\"file\"\>@
+inputFile :: forall m input err view ft. (Environment m input, FormInput input, FormError input err, ft ~ FileType input, Monoid ft)
+  => (FormId -> view)
+  -> Form m input err view (FileType input)
+inputFile = G.inputFile getFormId
+
+-- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes
+inputMulti :: forall m input err view a lbl. (FormError input err, FormInput input, Environment m input, Eq a)
+  => [(a, lbl)] -- ^ value, label, initially checked
+  -> (input -> Either err [a])
+  -> (FormId -> [G.Choice lbl a] -> view) -- ^ function which generates the view
+  -> (a -> Bool) -- ^ isChecked/isSelected initially
+  -> Form m input err view [a]
+inputMulti = G.inputMulti getFormId
+
+-- | radio buttons, single @\<select\>@ boxes
+inputChoice :: forall a m err input lbl view. (FormError input err, FormInput input, Environment m input, Eq a, Monoid view)
+  => (a -> Bool) -- ^ is default
+  -> NonEmpty (a, lbl) -- ^ value, label
+  -> (input -> Either err a)
+  -> (FormId -> [G.Choice lbl a] -> view) -- ^ function which generates the view
+  -> Form m input err view a
+inputChoice = G.inputChoice getFormId
+
+-- | this is necessary in order to basically map over the decoding function
+inputList :: forall m input err a view. (Monad m, FormError input err, Environment m input)
+  => (input -> m (Either err [a])) -- ^ decoding function for the list
+  -> ([view] -> view) -- ^ how to concatenate views
+  -> [a] -- ^ initial values
+  -> view -- ^ view to generate in the fail case
+  -> (a -> Form m input err view a)
+  -> Form m input err view [a]
+inputList = G.inputList getFormId
+
+-- | used to create @\<label\>@ elements
+label :: Environment m input
+  => (FormId -> view)
+  -> Form m input err view ()
+label = G.label getFormId
+
+-- | used to add a list of err 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 :: Environment m input
+  => ([err] -> view) -- ^ function to convert the err messages into a view
+  -> Form m input err view ()
+errors = G.errors
+
+-- | similar to 'errors' but includes err messages from children of the form as well.
+childErrors :: Environment m input
+  => ([err] -> view)
+  -> Form m input err view ()
+childErrors = G.childErrors
+
+-- | modify the view of a form based on its errors
+withErrors :: Environment m input
+  => (view -> [err] -> view)
+  -> Form m input err view a
+  -> Form m input err view a
+withErrors = G.withErrors
+
diff --git a/src/Ditto/Proof.hs b/src/Ditto/Proof.hs
--- a/src/Ditto/Proof.hs
+++ b/src/Ditto/Proof.hs
@@ -1,3 +1,14 @@
+{-# LANGUAGE 
+    DeriveFoldable
+  , DeriveFunctor
+  , DeriveTraversable
+  , GeneralizedNewtypeDeriving
+  , MultiParamTypeClasses 
+  , NamedFieldPuns
+  , ScopedTypeVariables
+  , StandaloneDeriving
+#-}
+
 {- |
 This module defines the 'Proof' type, some proofs, and some helper functions.
 
@@ -7,11 +18,13 @@
  - 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 Ditto.Proof where
 
 import Control.Monad.Trans (lift)
-import Ditto.Core (Form (..), Proved (..))
-import Ditto.Result (Result (..))
+import Ditto.Core (Form(..))
+import Ditto.Backend (FormError(..))
+import Ditto.Types (Proved(..), Result(..))
 import Numeric (readDec, readFloat, readSigned)
 
 -- | A 'Proof' attempts to prove something about a value.
@@ -22,73 +35,66 @@
 -- Generally, each 'Proof' has a unique data-type associated with it
 -- which names the proof, such as:
 --
---
-newtype Proof m error a b = Proof
-  { proofFunction :: a -> m (Either error b) -- ^ function which provides the proof
+
+data Proof m err a b = Proof
+  { proofFunction :: a -> m (Either err b) -- ^ function which provides the proof
+  , proofNewInitialValue :: a -> b
   }
 
 -- | apply a 'Proof' to a 'Form'
 prove
-  :: (Monad m)
+  :: (Monad m, Monoid view, FormError input error)
   => Form m input error view a
   -> Proof m error a b
   -> Form m input error view b
-prove (Form frm) (Proof f) =
-  Form $ do
-    (xml, mval) <- frm
-    val <- lift $ lift $ mval
-    case val of
-      (Error errs) -> pure (xml, pure $ Error errs)
-      (Ok (Proved posi a)) ->
-        do
-          r <- lift $ lift $ f a
-          case r of
-            (Left err) -> pure (xml, pure $ Error [(posi, err)])
-            (Right b) ->
-              pure
-                ( xml
-                , pure $
-                  Ok
-                    ( Proved
-                      { pos = posi
-                      , unProved = b
-                      }
-                    )
-                )
+prove (Form{formDecodeInput, formInitialValue, formFormlet}) (Proof f ivB) = Form 
+  (\input -> do 
+    a <- formDecodeInput input
+    case a of
+      Left x -> pure $ Left x
+      Right x -> f x
+  )
+  (fmap ivB formInitialValue)
+  ( do
+    (html, a) <- formFormlet
+    res <- lift $ case a of
+      Error xs -> pure $ Error xs
+      Ok (Proved pos x) -> do
+        eeb <- f x
+        case eeb of
+          Left err -> pure $ Error [(pos, err)]
+          Right res -> pure $ Ok (Proved pos res)
+    pure
+      ( html
+      , res
+      )
+  )
 
 -- * 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 a
-  -> Proof m error a b
-  -> Form m input error view b
-transform frm proof = frm `prove` proof
-
 -- | transform the 'Form' result using a monadic 'Either' function.
 transformEitherM
-  :: (Monad m)
+  :: (Monad m, Monoid view, FormError input error)
   => Form m input error view a
   -> (a -> m (Either error b))
+  -> (a -> b)
   -> Form m input error view b
-transformEitherM frm func = frm `transform` (Proof func)
+transformEitherM frm func ivb = frm `prove` (Proof func ivb)
 
 -- | transform the 'Form' result using an 'Either' function.
 transformEither
-  :: (Monad m)
+  :: (Monad m, Monoid view, FormError input error)
   => Form m input error view a
   -> (a -> Either error b)
+  -> (a -> b)
   -> Form m input error view b
-transformEither frm func = transformEitherM frm (pure . func)
+transformEither frm func ivb = transformEitherM frm (pure . func) ivb
 
 -- * Various Proofs
 
 -- | prove that a list is not empty
 notNullProof :: (Monad m) => error -> Proof m error [a] [a]
-notNullProof errorMsg = Proof (pure . check)
+notNullProof errorMsg = Proof (pure . check) id
   where
     check list =
       if null list
@@ -99,8 +105,9 @@
 decimal
   :: (Monad m, Eq i, Num i)
   => (String -> error) -- ^ create an error message ('String' is the value that did not parse)
+  -> i
   -> Proof m error String i
-decimal mkError = Proof (pure . toDecimal)
+decimal mkError i = Proof (pure . toDecimal) (const i)
   where
     toDecimal str =
       case readDec str of
@@ -108,8 +115,11 @@
         _ -> (Left $ mkError str)
 
 -- | read signed decimal number
-signedDecimal :: (Monad m, Eq i, Real i) => (String -> error) -> Proof m error String i
-signedDecimal mkError = Proof (pure . toDecimal)
+signedDecimal :: (Monad m, Eq i, Real i) 
+  => (String -> error) 
+  -> i
+  -> Proof m error String i
+signedDecimal mkError i = Proof (pure . toDecimal) (const i)
   where
     toDecimal str =
       case (readSigned readDec) str of
@@ -117,8 +127,11 @@
         _ -> (Left $ mkError str)
 
 -- | read 'RealFrac' number
-realFrac :: (Monad m, RealFrac a) => (String -> error) -> Proof m error String a
-realFrac mkError = Proof (pure . toRealFrac)
+realFrac :: (Monad m, RealFrac a) 
+  => (String -> error) 
+  -> a
+  -> Proof m error String a
+realFrac mkError a = Proof (pure . toRealFrac) (const a)
   where
     toRealFrac str =
       case readFloat str of
@@ -126,10 +139,14 @@
         _ -> (Left $ mkError str)
 
 -- | read a signed 'RealFrac' number
-realFracSigned :: (Monad m, RealFrac a) => (String -> error) -> Proof m error String a
-realFracSigned mkError = Proof (pure . toRealFrac)
+realFracSigned :: (Monad m, RealFrac a) 
+  => (String -> error) 
+  -> a
+  -> Proof m error String a
+realFracSigned mkError a = Proof (pure . toRealFrac) (const a)
   where
     toRealFrac str =
       case (readSigned readFloat) str of
         [(f, [])] -> (Right f)
         _ -> (Left $ mkError str)
+
diff --git a/src/Ditto/Result.hs b/src/Ditto/Result.hs
deleted file mode 100644
--- a/src/Ditto/Result.hs
+++ /dev/null
@@ -1,119 +0,0 @@
-{-# LANGUAGE DeriveFunctor #-}
-
--- | Module for the core result type, and related functions
---
-module Ditto.Result
-  ( Result (..)
-  , getResult
-  , FormId (..)
-  , zeroId
-  , mapId
-  , FormRange (..)
-  , incrementFormId
-  , unitRange
-  , isInRange
-  , isSubRange
-  , retainErrors
-  , retainChildErrors
-  )
-where
-
-import Data.List (intercalate)
-import Control.Applicative (Applicative (..))
-import Data.List.NonEmpty (NonEmpty(..))
-import qualified Data.List.NonEmpty as NE
-
--- | Type for failing computations
-data Result e ok
-  = Error [(FormRange, e)]
-  | Ok ok
-  deriving (Show, Eq, Functor)
-
-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
-      String -- ^ Global prefix for the form
-      (NonEmpty Int) -- ^ Stack indicating field. Head is most specific to this item
-  | FormIdCustom 
-      String -- ^ Local name of the input
-      Int -- ^ Index of the input
-  deriving (Eq, Ord)
-
--- | The zero ID, i.e. the first ID that is usable
-zeroId :: String -> FormId
-zeroId prefix = FormId prefix (pure 0)
-
--- | map a function over the @NonEmpty Int@ inside a 'FormId'
-mapId :: (NonEmpty Int -> NonEmpty Int) -> FormId -> FormId
-mapId f (FormId p is) = FormId p $ f is
-mapId _ x = x
-
-instance Show FormId where
-  show (FormId p xs) =
-    p ++ "-fval-" ++ (intercalate "." $ reverseMap show $ NE.toList xs)
-  show (FormIdCustom x _) = x
-
-reverseMap :: Foldable t => (a -> b) -> t a -> [b]
-reverseMap f = foldl (\as a -> f a : as ) []
-
--- | get the head 'Int' from a 'FormId'
-formId :: FormId -> Int
-formId (FormId _ (x :| _)) = x
-formId (FormIdCustom _ x) = x
-
--- | 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 x@FormIdCustom{} = x
-
--- | 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/src/Ditto/Types.hs b/src/Ditto/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Ditto/Types.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE 
+    BangPatterns
+  , DeriveFoldable
+  , DeriveFunctor
+  , DeriveTraversable
+  , GeneralizedNewtypeDeriving
+  , MultiParamTypeClasses 
+  , OverloadedStrings
+#-}
+
+-- | Types relevant to forms and their validation.
+module Ditto.Types (
+  -- * FormId
+    FormId(..)
+  , FormRange(..)
+  , encodeFormId
+  , formIdentifier
+  -- * Form result types
+  , Value(..)
+  , View(..)
+  , Proved(..)
+  , Result(..)
+  ) where
+
+import Control.Applicative (Alternative(..))
+import Data.List.NonEmpty (NonEmpty(..))
+import Data.String (IsString(..))
+import Data.Text (Text)
+import Torsor
+import qualified Data.Text as T
+
+------------------------------------------------------------------------------
+-- FormId
+------------------------------------------------------------------------------
+
+-- | An ID used to identify forms
+data FormId
+  = FormId
+      {-# UNPACK #-} !Text           -- ^ Global prefix for the form
+      {-# UNPACK #-} !(NonEmpty Int) -- ^ Stack indicating field. Head is most specific to this item
+  | FormIdName 
+      {-# UNPACK #-} !Text -- ^ Local name of the input
+      {-# UNPACK #-} !Int  -- ^ Index of the input
+  deriving (Eq, Ord, Show)
+
+instance IsString FormId where
+  fromString x = FormIdName (T.pack x) 0
+
+-- | Encoding a @FormId@: use this instead of @show@ for
+-- the name of the input / query string parameter
+encodeFormId :: FormId -> Text
+encodeFormId (FormId p xs) =
+  p <> "-val-" <> (T.intercalate "." $ foldr (\a as -> T.pack (show a) : as) [] xs)
+encodeFormId (FormIdName x _) = x
+
+-- | get the head 'Int' from a 'FormId'
+formIdentifier :: FormId -> Int
+formIdentifier (FormId _ (x :| _)) = x
+formIdentifier (FormIdName _ x) = x
+
+instance Torsor FormId Int where
+  add i (FormId p (x :| xs)) = FormId p $ (x + i) :| xs
+  add i (FormIdName n x) = FormIdName n $ x + i 
+  difference a b = formIdentifier a - formIdentifier b
+
+-- | A range of ID's to specify a group of forms
+data FormRange
+  = FormRange FormId FormId
+  deriving (Eq, Show)
+
+------------------------------------------------------------------------------
+-- Form result types
+------------------------------------------------------------------------------
+
+-- | views, values as a result of the environment, etc.
+
+-- | Function which creates the form view
+newtype View err v = View { unView :: [(FormRange, err)] -> v }
+  deriving (Semigroup, Monoid, Functor)
+
+-- | 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
+  deriving (Eq, Show, Functor, Traversable, Foldable)
+
+instance Applicative Value where
+  pure = Found
+  (Found f) <*> (Found x) = Found (f x)
+  Default <*> _ = Default
+  Missing <*> _ = Missing
+  Found{} <*> Default = Default
+  Found{} <*> Missing = Default
+
+instance Alternative Value where
+  empty = Missing
+  x@Found{} <|> _ = x
+  Default <|> _ = Default
+  Missing <|> x = x
+
+instance Semigroup a => Semigroup (Value a) where
+  Missing <> Missing = Missing
+  Default <> Missing = Default
+  Missing <> Default = Default
+  Default <> Default = Default
+  Found x <> Found y = Found (x <> y)
+  Found x <> _ = Found x
+  _ <> Found y = Found y
+
+-- | Type for failing computations
+-- Similar to @Either@ but with an accumilating @Applicative@ instance
+data Result e ok
+  = Error [(FormRange, e)]
+  | Ok ok
+  deriving (Show, Eq, Functor, Foldable, Traversable)
+
+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
+
+-- | Proved records a value, the location that value came from, and something that was proved about the value.
+data Proved a = Proved
+  { pos :: FormRange
+  , unProved :: a
+  } deriving (Show, Functor, Foldable, Traversable)
