diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@
 data Consumer s d a
 ```
 
+## Making descriptive consumers
+
 To make a consumer, this combinator is used:
 
 ``` haskell
@@ -37,12 +39,15 @@
 strings, a Map, a Vector, etc. You may or may not decide to modify the
 state during generation of the description and during parsing.
 
+## Running descriptive consumers
+
 To use a consumer or describe what it does, these are used:
 
 ``` haskell
 consume :: Consumer s d a -- ^ The consumer to run.
         -> s -- ^ Initial state.
         -> Result (Description d) a
+
 describe :: Consumer s d a -- ^ The consumer to run.
          -> s -- ^ Initial state. Can be \"empty\" if you don't use it for
               -- generating descriptions.
@@ -56,12 +61,15 @@
             => Consumer s d a -- ^ The consumer to run.
             -> StateT s m (Result (Description d) a)
 runConsumer (Consumer _ m) = m
+
 runDescription :: Monad m
                => Consumer s d a -- ^ The consumer to run.
                -> StateT s m (Description d)
 runDescription (Consumer desc _) = desc
 ```
 
+## Descriptions
+
 A description is like this:
 
 ``` haskell
@@ -85,6 +93,24 @@
 describeParser :: Description Text -> Text
 describeForm :: Description (Html ()) -> Html ()
 describeArgs :: Description CmdArgs -> Text
+```
+
+## Wrapping
+
+One can wrap up a consumer to alter either the description or the
+parser or both, this can be used for wrapping labels, or adding
+validation, things of that nature:
+
+``` haskell
+wrap :: (forall m. Monad m => StateT t m (Description d)
+                           -> StateT s m (Description d))
+     -- ^ Transform the description.
+     -> (forall m. Monad m => StateT t m (Description d)
+                           -> StateT t m (Result (Description d) a)
+                           -> StateT s m (Result (Description d) b))
+     -- ^ Transform the parser. Can re-run the parser as many times as desired.
+     -> Consumer t d a
+     -> Consumer s d b
 ```
 
 See below for some examples of this library.
diff --git a/descriptive.cabal b/descriptive.cabal
--- a/descriptive.cabal
+++ b/descriptive.cabal
@@ -1,5 +1,5 @@
 name:                descriptive
-version:             0.5.0
+version:             0.6.0
 synopsis:            Self-describing consumers/parsers; forms, cmd-line args, JSON, etc.
 description:         Self-describing consumers/parsers. See the README.md for more information. It is currently EXPERIMENTAL.
 stability:           Experimental
diff --git a/src/Descriptive.hs b/src/Descriptive.hs
--- a/src/Descriptive.hs
+++ b/src/Descriptive.hs
@@ -21,7 +21,8 @@
   ,Result(..)
   -- * Combinators
   ,consumer
-  ,wrap)
+  ,wrap
+  ,validate)
   where
 
 import Control.Applicative
@@ -267,3 +268,26 @@
 wrap redescribe reparse (Consumer d p) =
   Consumer (redescribe d)
            (reparse d p)
+
+-- | Add validation to a consumer.
+validate :: d                                              -- ^ Description of what it expects.
+         -> (forall m. MonadState s m => a -> m (Maybe b)) -- ^ Attempt to parse the value.
+         -> Consumer s d a                                 -- ^ Consumer to add validation to.
+         -> Consumer s d b                                 -- ^ A new validating consumer.
+validate d' check =
+  wrap (liftM wrapper)
+       (\d p ->
+          do s <- get
+             r <- p
+             case r of
+               (Failed e) -> return (Failed e)
+               (Continued e) ->
+                 return (Continued (wrapper e))
+               (Succeeded a) ->
+                 do r' <- check a
+                    case r' of
+                      Nothing ->
+                        do doc <- withStateT (const s) d
+                           return (Continued (wrapper doc))
+                      Just a' -> return (Succeeded a'))
+  where wrapper = Wrap d'
diff --git a/src/Descriptive/Form.hs b/src/Descriptive/Form.hs
--- a/src/Descriptive/Form.hs
+++ b/src/Descriptive/Form.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE FlexibleContexts #-}
 
@@ -6,7 +7,6 @@
 module Descriptive.Form
   (-- * Combinators
    input
-  ,validate
   -- * Description
   ,Form (..)
   )
@@ -34,25 +34,3 @@
                          Nothing -> Continued d
                          Just a -> Succeeded a))
   where d = Unit (Input name)
-
--- | Validate a form input with a description of what's required.
-validate :: Text -- ^ Description of what it expects.
-         -> (a -> Maybe b) -- ^ Attempt to parse the value.
-         -> Consumer (Map Text Text) Form a -- ^ Consumer to add validation to.
-         -> Consumer (Map Text Text) Form b
-validate d' check =
-  wrap (liftM wrapper)
-       (\d p ->
-          do s <- get
-             r <- p
-             case r of
-               (Failed e) -> return (Failed e)
-               (Continued e) ->
-                 return (Continued (wrapper e))
-               (Succeeded a) ->
-                 case check a of
-                   Nothing ->
-                     do doc <- withStateT (const s) d
-                        return (Continued (wrapper doc))
-                   Just a' -> return (Succeeded a'))
-  where wrapper = Wrap (Constraint d')
diff --git a/src/test/Main.hs b/src/test/Main.hs
--- a/src/test/Main.hs
+++ b/src/test/Main.hs
@@ -94,12 +94,12 @@
                     [("password2","gob"),("password","go"),("token","woot")]) ==
          Succeeded "woot")
   where login =
-          Form.validate
-            "confirmed password (entered the same twice)"
+          validate
+            (Form.Constraint "confirmed password (entered the same twice)")
             (\(x,y) ->
-               if x == y
-                  then Just y
-                  else Nothing)
+               return (if x == y
+                          then Just y
+                          else Nothing))
             ((,) <$>
              Form.input "password" <*>
              Form.input "password2") <|>
