diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,48 +4,3 @@
 The set of possible handlers can be restricted by "predicates",
 which operate on WAI requests and have to be true or else the
 handler will not be called.
-
-For details have a look at the haddock documentation of
-`Network.Wai.Routing.Tutorial` or the `examples` folder in the
-source distribution.
-
-This library is a port of `snap-predicates` which provides
-similar functionality for the snap framework.
-
-The routing tree construction is implemented using `wai-route`.
-
-Here is a simple usage example.
-
-
-```haskell
-import Data.ByteString (ByteString)
-import Data.Text (Text)
-import Network.Wai
-import Network.Wai.Routing
-import Network.Wai.Handler.Warp
-
-main :: IO ()
-main = run 8080 (route (prepare start))
-
-start :: Monad m => Routes a m ()
-start = do
-    get "/user/:name" fetchUser $
-        capture "name"
-
-    get "/user/find" findUser $
-        query "byName" :||: query "byId"
-
-    delete "/user/:name" rmUser $
-        capture "name" :&: opt (cookie "foo")
-
-fetchUser :: Monad m => Text -> m Response
-fetchUser name = ...
-
-findUser :: Monad m => Either ByteString Word64 -> m Response
-findUser (Left  name)  = ...
-findUser (Right ident) = ...
-
-rmUser :: Monad m => Text ::: Maybe Int -> m Response
-rmUser (name ::: foo)  = ...
-```
-
diff --git a/examples/eval-routing.hs b/examples/eval-routing.hs
--- a/examples/eval-routing.hs
+++ b/examples/eval-routing.hs
@@ -10,6 +10,7 @@
 import Data.String
 import Network.HTTP.Types
 import Network.Wai
+import Network.Wai.Predicate
 import Network.Wai.Routing
 import Network.Wai.Handler.Warp
 import Network.Wai.Middleware.RequestLogger
@@ -31,7 +32,7 @@
 main = run 8080 $ logStdout (route (prepare start))
 
 start :: Monad m => Routes a m ()
-start = get "eval" eval (query "x" :&: query "y" :&: query "f")
+start = get "eval" eval (query "x" .&. query "y" .&. query "f")
 
 eval :: Monad m => Int ::: Int ::: Op -> m Response
 eval (x ::: y ::: f) = respond status200 . fromString . show $
diff --git a/src/Network/Wai/Routing.hs b/src/Network/Wai/Routing.hs
--- a/src/Network/Wai/Routing.hs
+++ b/src/Network/Wai/Routing.hs
@@ -4,12 +4,10 @@
 
 module Network.Wai.Routing
     ( module Network.Wai.Routing.Route
-    , module Network.Wai.Routing.Error
     , module Network.Wai.Routing.Predicate
-    , module Network.Wai.Routing.Request
+    , HasCaptures (..)
     ) where
 
-import Network.Wai.Routing.Route
-import Network.Wai.Routing.Error
 import Network.Wai.Routing.Predicate
 import Network.Wai.Routing.Request
+import Network.Wai.Routing.Route
diff --git a/src/Network/Wai/Routing/Error.hs b/src/Network/Wai/Routing/Error.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Error.hs
+++ /dev/null
@@ -1,19 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-module Network.Wai.Routing.Error where
-
-import Data.ByteString (ByteString)
-import Network.HTTP.Types.Status
-
--- | The error type used as meta-data for @F@ in all WAI predicates.
-data Error = Error
-    { status  :: !Status          -- ^ HTTP status code
-    , message :: Maybe ByteString -- ^ optional status message
-    } deriving (Eq, Show)
-
--- | Convenience function to construct 'Error' values from
--- status code and body message.
-err :: Status -> ByteString -> Error
-err s = Error s . Just
diff --git a/src/Network/Wai/Routing/Internal.hs b/src/Network/Wai/Routing/Internal.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Internal.hs
+++ /dev/null
@@ -1,37 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeFamilies      #-}
-
-module Network.Wai.Routing.Internal
-    ( readValues
-    , rqApply
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Data.List (foldl')
-import Data.String (fromString)
-import Network.HTTP.Types
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-readValues :: FromByteString a => [ByteString] -> Either ByteString a
-readValues = foldl' result (Left "no parse") . map (runParser parser)
-  where
-    result (Left  _) (Right x) = Right x
-    result (Right x) _         = Right x
-    result _         (Left  x) = Left (fromString x)
-
-rqApply :: (Req -> [ByteString])
-        -> ([ByteString] -> Either ByteString a)
-        -> Error
-        -> Req
-        -> Boolean Error a
-rqApply f reader e r =
-    case f r of
-        [] -> F e
-        vs -> either (F . err status400) (T 0) $ reader vs
diff --git a/src/Network/Wai/Routing/MediaType.hs b/src/Network/Wai/Routing/MediaType.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/MediaType.hs
+++ /dev/null
@@ -1,31 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE DataKinds      #-}
-{-# LANGUAGE KindSignatures #-}
-
-module Network.Wai.Routing.MediaType where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.Char8 (pack)
-import GHC.TypeLits
-
-data Media (t :: Symbol) (s :: Symbol) = Media
-    { rawType      :: !ByteString
-    , rawSubTypes  :: !ByteString
-    , mediaQuality :: !Double
-    , mediaParams  :: ![(ByteString, ByteString)]
-    } deriving (Eq, Show)
-
-mediaType :: SingI t => Media t s -> ByteString
-mediaType m = withSing (f m)
-  where
-    f :: Media t s -> Sing t -> ByteString
-    f _ t = pack (fromSing t)
-
-mediaSubType :: SingI s => Media t s -> ByteString
-mediaSubType m = withSing (f m)
-  where
-    f :: Media t s -> Sing s -> ByteString
-    f _ s = pack (fromSing s)
diff --git a/src/Network/Wai/Routing/Parser/MediaType.hs b/src/Network/Wai/Routing/Parser/MediaType.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Parser/MediaType.hs
+++ /dev/null
@@ -1,66 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings #-}
-
-module Network.Wai.Routing.Parser.MediaType
-    ( MediaType (..)
-    , readMediaTypes
-    ) where
-
-import Control.Applicative
-import Data.Attoparsec.Char8
-import Data.ByteString (ByteString)
-import Data.List (sortBy)
-import Network.Wai.Routing.Parser.Shared
-import Network.Wai.Routing.Request
-
-data MediaType = MediaType
-    { medType    :: !ByteString
-    , medSubtype :: !ByteString
-    , medQuality :: !Double
-    , medParams  :: ![(ByteString, ByteString)]
-    } deriving (Eq, Show)
-
-readMediaTypes :: ByteString -> Req -> [MediaType]
-readMediaTypes k r =
-    sortBy q . concatMap parseMediaTypes $ lookupHeader k r
-  where
-    q a b = medQuality b `compare` medQuality a
-
-parseMediaTypes :: ByteString -> [MediaType]
-parseMediaTypes = either (const []) id . parseOnly mediaTypes
-
-mediaTypes :: Parser [MediaType]
-mediaTypes = mediaType `sepBy` char ','
-
-mediaType :: Parser MediaType
-mediaType =
-    toMediaType <$> trim typ <*> (char '/' *> trim subtyp) <*> params
-  where
-    toMediaType t s p =
-        case lookup "q" p >>= toDouble of
-            Just q  -> MediaType t s q (filter ((/= "q") . fst) p)
-            Nothing -> MediaType t s 1.0 p
-
-params :: Parser [(ByteString, ByteString)]
-params = (trim (char ';') *> (element `sepBy` trim (char ';'))) <|> return []
-  where
-    element = (,) <$> trim key <*> (char '=' *> trim val)
-
-typ, subtyp, key, val :: Parser ByteString
-typ    = takeTill (oneof "/ ")
-subtyp = takeTill (oneof ",; ")
-
-key = do
-    c <- peekChar
-    if c == Just ',' then fail "comma" else takeTill (oneof "= ")
-
-val = takeTill (oneof ",; ")
-
-toDouble :: ByteString -> Maybe Double
-toDouble bs = toMaybe (parseOnly double bs)
-  where
-    toMaybe (Right x) = Just x
-    toMaybe (Left  _) = Nothing
diff --git a/src/Network/Wai/Routing/Parser/Shared.hs b/src/Network/Wai/Routing/Parser/Shared.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Parser/Shared.hs
+++ /dev/null
@@ -1,22 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings #-}
-
-module Network.Wai.Routing.Parser.Shared where
-
-import Control.Applicative
-import Data.Attoparsec.Char8
-import Data.ByteString (ByteString)
-
-import qualified Data.ByteString.Char8 as C
-
-spaces :: Parser ()
-spaces = skipWhile (== ' ')
-
-trim :: Parser a -> Parser a
-trim p = spaces *> p <* spaces
-
-oneof :: ByteString -> Char -> Bool
-oneof s c = C.any (== c) s
diff --git a/src/Network/Wai/Routing/Predicate.hs b/src/Network/Wai/Routing/Predicate.hs
--- a/src/Network/Wai/Routing/Predicate.hs
+++ b/src/Network/Wai/Routing/Predicate.hs
@@ -2,23 +2,38 @@
 -- License, v. 2.0. If a copy of the MPL was not distributed with this
 -- file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-module Network.Wai.Routing.Predicate
-    ( module Network.Wai.Routing.Predicate.Accept
-    , module Network.Wai.Routing.Predicate.Capture
-    , module Network.Wai.Routing.Predicate.Content
-    , module Network.Wai.Routing.Predicate.Cookie
-    , module Network.Wai.Routing.Predicate.Header
-    , module Network.Wai.Routing.Predicate.Param
-    , module Network.Wai.Routing.Predicate.Predicate
-    , module Network.Wai.Routing.Predicate.Query
-    ) where
+{-# LANGUAGE OverloadedStrings #-}
 
-import Network.Wai.Routing.Predicate.Accept
-import Network.Wai.Routing.Predicate.Capture
-import Network.Wai.Routing.Predicate.Content
-import Network.Wai.Routing.Predicate.Cookie
-import Network.Wai.Routing.Predicate.Header
-import Network.Wai.Routing.Predicate.Param
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Predicate.Query
+-- | 'Predicate's which are specific to @wai-routing@.
+-- Please note that these can be freely combined with other predicates from
+-- @wai-predicates@.
+module Network.Wai.Routing.Predicate where
 
+import Data.ByteString (ByteString)
+import Data.ByteString.From
+import Data.Monoid
+import Network.HTTP.Types
+import Network.Wai.Predicate
+import Network.Wai.Predicate.Request
+import Network.Wai.Predicate.Utility
+import Network.Wai.Routing.Request
+
+-- | Request path parameters.
+capture :: (HasCaptures r, FromByteString a) => ByteString -> Predicate r Error a
+capture k r = case lookupCapture k r of
+    [] -> Fail (err status400 ("Missing path parameter '" <> k <> "'."))
+    cc -> either (Fail . err status400) return (readValues cc)
+
+-- | Request path parameters.
+hasCapture :: (HasCaptures r) => ByteString -> Predicate r Error ()
+hasCapture k r =
+    if null (lookupCapture k r)
+        then Fail (err status400 ("Missing path parameter '" <> k <> "'."))
+        else return ()
+
+-- | @param \"foo\"@ is equivalent to @query \"foo\" .|. capture \"foo\"@
+param :: (HasCaptures r, HasQuery r, FromByteString a) => ByteString -> Predicate r Error a
+param k = query k .|. capture k
+
+hasParam :: (HasCaptures r, HasQuery r) => ByteString -> Predicate r Error ()
+hasParam k = hasQuery k .|. hasCapture k
diff --git a/src/Network/Wai/Routing/Predicate/Accept.hs b/src/Network/Wai/Routing/Predicate/Accept.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Accept.hs
+++ /dev/null
@@ -1,68 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE DataKinds             #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-
-module Network.Wai.Routing.Predicate.Accept
-    ( Accept
-    , accept
-    , module Network.Wai.Routing.MediaType
-    ) where
-
-import Control.Monad
-import Data.ByteString (ByteString)
-import Data.ByteString.Char8 (pack)
-import Data.Monoid hiding (All)
-import GHC.TypeLits
-import Data.Maybe
-import Network.HTTP.Types
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-import Network.Wai.Routing.MediaType
-
-import qualified Network.Wai.Routing.Parser.MediaType as M
-
--- | A 'Predicate' against the 'Request's \"Accept\" header.
-data Accept (t :: Symbol) (s :: Symbol) = Accept
-
-accept :: Accept t s
-accept = Accept
-{-# INLINABLE accept #-}
-
-type1 :: SingI t => Accept t s -> ByteString
-type1 m = withSing (f m)
-  where
-    f :: Accept t s -> Sing t -> ByteString
-    f _ t = pack $ fromSing t
-
-type2 :: SingI s => Accept t s -> ByteString
-type2 m = withSing (f m)
-  where
-    f :: Accept t s -> Sing s -> ByteString
-    f _ s = pack $ fromSing s
-
-instance (SingI t, SingI s) => Predicate (Accept t s) Req where
-    type FVal (Accept t s) = Error
-    type TVal (Accept t s) = Media t s
-    apply a r = let mtypes = M.readMediaTypes "accept" r in
-        if null mtypes
-            then T 0 (Media (type1 a) (type2 a) 1.0 [])
-            else case findMediaType a mtypes of
-                m:_ -> T (1.0 - mediaQuality m) m
-                []  -> F (err status406 msg)
-      where
-        msg = "Expected 'Accept: " <> type1 a <> "/" <> type2 a <> "'."
-
-findMediaType :: (SingI t, SingI s) => Accept t s -> [M.MediaType] -> [Media t s]
-findMediaType a = mapMaybe (\m -> do
-    let at = type1 a
-        as = type2 a
-        mt = M.medType m
-        ms = M.medSubtype m
-    guard (mt == "*" || at == mt && ms == "*" || as == ms)
-    return $ Media at as (M.medQuality m) (M.medParams m))
diff --git a/src/Network/Wai/Routing/Predicate/Capture.hs b/src/Network/Wai/Routing/Predicate/Capture.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Capture.hs
+++ /dev/null
@@ -1,62 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
--- | Predicates against path parameters.
--- When declaring routes, paths may contain \"variables\" which
--- capture whatever is given at that position by an actual request.
--- For example:
---
--- @
--- get \"\/user\/:name\/address\/:street\" handler $
---     Capture \"name\" :&: Capture \"street\"
--- @
---
--- extracts from a request path whatever is given for @:name@
--- and @:street@.
-module Network.Wai.Routing.Predicate.Capture
-    ( Capture
-    , HasCapture
-    , capture
-    , hasCapture
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Data.Monoid
-import Network.HTTP.Types.Status
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Internal
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-newtype Capture a = Capture ByteString
-
-capture :: ByteString -> Capture a
-capture = Capture
-{-# INLINABLE capture #-}
-
-instance (FromByteString a) => Predicate (Capture a) Req where
-    type FVal (Capture a) = Error
-    type TVal (Capture a) = a
-    apply (Capture x)     =
-        let msg = "Missing path parameter '" <> x <> "'." in
-        rqApply (lookupCapture x) readValues (err status400 msg)
-
-newtype HasCapture = HasCapture ByteString
-
-hasCapture :: ByteString -> HasCapture
-hasCapture = HasCapture
-{-# INLINABLE hasCapture #-}
-
-instance Predicate HasCapture Req where
-    type FVal HasCapture   = Error
-    type TVal HasCapture   = ()
-    apply (HasCapture x) r =
-        if null (lookupCapture x r)
-            then F (err status400 ("Missing path parameter '" <> x <> "'."))
-            else T 0 ()
diff --git a/src/Network/Wai/Routing/Predicate/Content.hs b/src/Network/Wai/Routing/Predicate/Content.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Content.hs
+++ /dev/null
@@ -1,71 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE DataKinds             #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-
-module Network.Wai.Routing.Predicate.Content
-    ( ContentType
-    , contentType
-    , module Network.Wai.Routing.MediaType
-    ) where
-
-import Control.Monad
-import Data.ByteString (ByteString)
-import Data.ByteString.Char8 (pack)
-import Data.Monoid hiding (All)
-import GHC.TypeLits
-import Data.Maybe
-import Network.HTTP.Types.Status
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.MediaType
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-import qualified Network.Wai.Routing.Parser.MediaType as M
-
--- | A 'Predicate' against the 'Request's \"Content-Type\" header.
-data ContentType (t :: Symbol) (s :: Symbol) = ContentType
-
-contentType :: ContentType t s
-contentType = ContentType
-{-# INLINABLE contentType #-}
-
-type1 :: SingI t => ContentType t s -> ByteString
-type1 m = withSing (f m)
-  where
-    f :: ContentType t s -> Sing t -> ByteString
-    f _ t = pack $ fromSing t
-
-type2 :: SingI s => ContentType t s -> ByteString
-type2 m = withSing (f m)
-  where
-    f :: ContentType t s -> Sing s -> ByteString
-    f _ s = pack $ fromSing s
-
-instance (SingI t, SingI s) => Predicate (ContentType t s) Req where
-    type FVal (ContentType t s) = Error
-    type TVal (ContentType t s) = Media t s
-    apply c r = let mtypes = M.readMediaTypes "content-type" r in
-        case findContentType c mtypes of
-            m:_ -> T (1.0 - mediaQuality m) m
-            []  -> F (err status415 msg)
-      where
-        msg = "Expected 'Content-Type: " <> type1 c <> "/" <> type2 c <> "'."
-
-findContentType :: (SingI t, SingI s) => ContentType t s -> [M.MediaType] -> [Media t s]
-findContentType c = mapMaybe (\m -> do
-    let ct = type1 c
-        cs = type2 c
-        mt = M.medType m
-        ms = M.medSubtype m
-    guard (ct == "*" || ct == mt && cs == "*" || cs == ms)
-    return $ Media mt ms (quality ct cs) (M.medParams m))
-  where
-    quality "*" "*" = 0
-    quality "*"  _  = 0.2
-    quality  _  "*" = 0.5
-    quality  _   _  = 1.0
diff --git a/src/Network/Wai/Routing/Predicate/Cookie.hs b/src/Network/Wai/Routing/Predicate/Cookie.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Cookie.hs
+++ /dev/null
@@ -1,52 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
-module Network.Wai.Routing.Predicate.Cookie
-    ( Cookie
-    , HasCookie
-    , cookie
-    , hasCookie
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Data.Monoid
-import Network.HTTP.Types.Status
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Internal
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-newtype Cookie a = Cookie ByteString
-
-cookie :: ByteString -> Cookie a
-cookie = Cookie
-{-# INLINABLE cookie #-}
-
-instance (FromByteString a) => Predicate (Cookie a) Req where
-    type FVal (Cookie a) = Error
-    type TVal (Cookie a) = a
-    apply (Cookie x)     =
-        rqApply (lookupCookie x) readValues (err status400 (msg x))
-
-newtype HasCookie = HasCookie ByteString
-
-hasCookie :: ByteString -> HasCookie
-hasCookie = HasCookie
-{-# INLINABLE hasCookie #-}
-
-instance Predicate HasCookie Req where
-    type FVal HasCookie   = Error
-    type TVal HasCookie   = ()
-    apply (HasCookie x) r =
-        if null (lookupCookie x r)
-            then F (err status400 (msg x))
-            else T 0 ()
-
-msg :: ByteString -> ByteString
-msg x = "Missing cookie '" <> x <> "'."
diff --git a/src/Network/Wai/Routing/Predicate/Header.hs b/src/Network/Wai/Routing/Predicate/Header.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Header.hs
+++ /dev/null
@@ -1,54 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies          #-}
-
-module Network.Wai.Routing.Predicate.Header
-    ( Hdr
-    , HasHdr
-    , hdr
-    , hasHdr
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Data.CaseInsensitive (mk)
-import Data.List (find)
-import Data.Maybe
-import Data.Monoid
-import Network.HTTP.Types.Status
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Internal
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-newtype Hdr a = Hdr ByteString
-
-hdr :: ByteString -> Hdr a
-hdr = Hdr
-{-# INLINABLE hdr #-}
-
-instance (FromByteString a) => Predicate (Hdr a) Req where
-    type FVal (Hdr a) = Error
-    type TVal (Hdr a) = a
-    apply (Hdr x)     =
-        let msg = "Missing header '" <> x <> "'." in
-        rqApply (lookupHeader x) readValues (err status400 msg)
-
-newtype HasHdr = HasHdr ByteString
-
-hasHdr :: ByteString -> HasHdr
-hasHdr = HasHdr
-{-# INLINABLE hasHdr #-}
-
-instance Predicate HasHdr Req where
-    type FVal HasHdr   = Error
-    type TVal HasHdr   = ()
-    apply (HasHdr x) r =
-        if isJust $ find ((mk x ==) . fst) (headers r)
-            then T 0 ()
-            else F (err status400 ("Missing header '" <> x <> "'."))
-
diff --git a/src/Network/Wai/Routing/Predicate/Param.hs b/src/Network/Wai/Routing/Predicate/Param.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Param.hs
+++ /dev/null
@@ -1,45 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
-module Network.Wai.Routing.Predicate.Param
-    ( Param
-    , HasParam
-    , param
-    , hasParam
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Request
-import Network.Wai.Routing.Predicate.Capture
-import Network.Wai.Routing.Predicate.Query
-import Network.Wai.Routing.Predicate.Predicate
-
--- | @Param \"x\"@ is equivalent to @'Query' \"x\" ':|:' 'Capture' \"x\"@.
-newtype Param a = Param ByteString
-
-param :: ByteString -> Param a
-param = Param
-{-# INLINABLE param #-}
-
-instance (FromByteString a) => Predicate (Param a) Req where
-    type FVal (Param a) = Error
-    type TVal (Param a) = a
-    apply (Param x)     = apply (query x :|: capture x)
-
-newtype HasParam = HasParam ByteString
-
-hasParam :: ByteString -> HasParam
-hasParam = HasParam
-{-# INLINABLE hasParam #-}
-
-instance Predicate HasParam Req where
-    type FVal HasParam = Error
-    type TVal HasParam = ()
-    apply (HasParam x) = apply (hasQuery x :|: hasCapture x)
-
diff --git a/src/Network/Wai/Routing/Predicate/Predicate.hs b/src/Network/Wai/Routing/Predicate/Predicate.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Predicate.hs
+++ /dev/null
@@ -1,240 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE FlexibleInstances     #-}
-{-# LANGUAGE GADTs                 #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE TypeOperators         #-}
-
-module Network.Wai.Routing.Predicate.Predicate
-    ( Delta
-    , Boolean   (..)
-    , Predicate (..)
-    , (:|:)     (..)
-    , (:&:)     (..)
-    , (:||:)    (..)
-    , (:::)     (..)
-    , (:+:)
-    , Const
-    , Fail
-    , Opt
-    , Def
-    , PMap
-    , PMapT
-    , PMapF
-
-    , constant
-    , failure
-    , true
-    , opt
-    , def
-    , pmap
-    , pmapT
-    , pmapF
-    , with
-    ) where
-
-import Prelude hiding (and, or)
-
--- | 'Delta' is a measure of distance. It is (optionally)
--- used in predicates that evaluate to 'T' but not uniquely so, i.e.
--- different evaluations of 'T' are possible and they may have a different
--- \"fitness\".
---
--- An example is content-negotiation. A HTTP request may specify
--- a preference list of various media-types. A predicate matching one
--- specific media-type evaluates to 'T', but other media-types may match
--- even better. To represent this ambivalence, the predicate will include
--- a delta value which can be used to decide which of the matching
--- predicates should be preferred.
-type Delta = Double
-
--- | A 'Bool'-like type where each branch 'T'rue or 'F'alse carries
--- some meta-data which is threaded through 'Predicate' evaluation.
-data Boolean f t
-    = F f       -- ^ logical False with some meta-data
-    | T Delta t -- ^ logical True with some meta-data
-    deriving (Eq, Show)
-
--- | The 'Predicate' class declares the function 'apply' which
--- evaluates the predicate against some value, returning a value
--- of type 'Boolean'.
--- Besides being parameterised over predicate type and predicate
--- parameter, the class is also parameterised over the actual types
--- of T's and F's meta-data.
-class Predicate p a where
-    type FVal p
-    type TVal p
-    apply :: p -> a -> Boolean (FVal p) (TVal p)
-
--- | A 'Predicate' instance which always returns 'T' with
--- the given value as T's meta-data.
-data Const f t where
-    Const :: t -> Const f t
-
-instance Predicate (Const f t) a where
-    type FVal (Const f t) = f
-    type TVal (Const f t) = t
-    apply (Const a) _     = T 0 a
-
-constant :: t -> Const f t
-constant = Const
-{-# INLINABLE constant #-}
-
-true :: Const a ()
-true = Const ()
-{-# INLINABLE true #-}
-
--- | A 'Predicate' instance which always returns 'F' with
--- the given value as F's meta-data.
-data Fail f t where
-    Fail :: f -> Fail f t
-
-failure :: f -> Fail f t
-failure = Fail
-{-# INLINABLE failure #-}
-
-instance Predicate (Fail f t) a where
-    type FVal (Fail f t) = f
-    type TVal (Fail f t) = t
-    apply (Fail a) _     = F a
-
--- | A 'Predicate' instance corresponding to the logical
--- OR connective of two 'Predicate's. It requires the
--- meta-data of each 'T'rue branch to be of the same type.
---
--- If both arguments evaluate to 'T' the one with the
--- smaller 'Delta' will be preferred, or--if equal--the
--- left-hand argument.
-data a :|: b = a :|: b
-
-instance (Predicate a c, Predicate b c, TVal a ~ TVal b, FVal a ~ FVal b) => Predicate (a :|: b) c
-  where
-    type FVal (a :|: b) = FVal a
-    type TVal (a :|: b) = TVal a
-    apply (a :|: b) r   = apply a r `or` apply b r
-      where
-        or x@(T d0 _) y@(T d1 _) = if d1 < d0 then y else x
-        or x@(T _  _)   (F    _) = x
-        or (F      _) x@(T _  _) = x
-        or (F      _) x@(F    _) = x
-
-type a :+: b = Either a b
-
--- | A 'Predicate' instance corresponding to the logical
--- OR connective of two 'Predicate's. The meta-data of
--- each 'T'rue branch can be of different types.
---
--- If both arguments evaluate to 'T' the one with the
--- smaller 'Delta' will be preferred, or--if equal--the
--- left-hand argument.
-data a :||: b = a :||: b
-
-instance (Predicate a c, Predicate b c, FVal a ~ FVal b) => Predicate (a :||: b) c
-  where
-    type FVal (a :||: b) = FVal a
-    type TVal (a :||: b) = TVal a :+: TVal b
-    apply (a :||: b) r   = apply a r `or` apply b r
-      where
-        or (T d0 t0) (T d1 t1) = if d1 < d0 then T d1 (Right t1) else T d0 (Left t0)
-        or (T  d  t) (F     _) = T d (Left t)
-        or (F     _) (T  d  t) = T d (Right t)
-        or (F     _) (F     f) = F f
-
--- | Data-type used for tupling-up the results of ':&:'.
-data a ::: b = a ::: b deriving (Eq, Show)
-
--- | A 'Predicate' instance corresponding to the logical
--- AND connective of two 'Predicate's.
-data a :&: b = a :&: b
-
-instance (Predicate a c, Predicate b c, FVal a ~ FVal b) => Predicate (a :&: b) c
-  where
-    type FVal (a :&: b) = FVal a
-    type TVal (a :&: b) = TVal a ::: TVal b
-    apply (a :&: b) r   = apply a r `and` apply b r
-      where
-        and (T d x) (T w y) = T (d + w) (x ::: y)
-        and (T _ _) (F   f) = F f
-        and (F   f) _       = F f
-
--- | A 'Predicate' modifier which makes the underlying predicate optional,
--- i.e. the 'TVal' becomes a 'Maybe' and in the failure-case 'Nothing' is
--- returned.
-newtype Opt a = Opt a
-
-opt :: a -> Opt a
-opt = Opt
-{-# INLINABLE opt #-}
-
-instance (Predicate a b) => Predicate (Opt a) b where
-    type FVal (Opt a) = FVal a
-    type TVal (Opt a) = Maybe (TVal a)
-    apply (Opt a) r   = case apply a r of
-        T d x -> T d (Just x)
-        F _   -> T 0 Nothing
-
--- | A 'Predicate' modifier which returns as 'TVal' the provided default
--- value if the underlying predicate fails.
-data Def d a = Def d a
-
-def :: d -> a -> Def d a
-def = Def
-{-# INLINABLE def #-}
-
-instance (Predicate a b, d ~ TVal a) => Predicate (Def d a) b where
-    type FVal (Def d a) = FVal a
-    type TVal (Def d a) = TVal a
-    apply (Def d a) r   = case apply a r of
-        T n x -> T n x
-        F _   -> T 0 d
-
--- | A 'Predicate' function, i.e. a function of the underlying predicate's
--- result.
-data PMap a f t = PMap (Boolean (FVal a) (TVal a) -> Boolean f t) a
-
-pmap :: (Boolean (FVal a) (TVal a) -> Boolean f t) -> a -> PMap a f t
-pmap = PMap
-{-# INLINABLE pmap #-}
-
-instance (Predicate a b) => Predicate (PMap a f t) b where
-    type FVal (PMap a f t) = f
-    type TVal (PMap a f t) = t
-    apply (PMap f a) r     = f $ apply a r
-
--- | Like 'PMap' but a function of the underlying predicate's 'TVal'.
-data PMapT a t = PMapT (TVal a -> Boolean (FVal a) t) a
-
-pmapT :: (TVal a -> Boolean (FVal a) t) -> a -> PMapT a t
-pmapT = PMapT
-{-# INLINABLE pmapT #-}
-
-instance (Predicate a b) => Predicate (PMapT a t) b where
-    type FVal (PMapT a t) = FVal a
-    type TVal (PMapT a t) = t
-    apply (PMapT f a) r   = case apply a r of
-        (T _ x) -> f x
-        (F x)   -> F x
-
--- | Like 'PMap' but a function of the underlying predicate's 'FVal'.
-data PMapF a f = PMapF (FVal a -> Boolean f (TVal a)) a
-
-pmapF :: (FVal a -> Boolean f (TVal a)) -> a -> PMapF a f
-pmapF = PMapF
-{-# INLINABLE pmapF #-}
-
-instance (Predicate a b) => Predicate (PMapF a f) b where
-    type FVal (PMapF a f) = f
-    type TVal (PMapF a f) = TVal a
-    apply (PMapF f a) r   = case apply a r of
-        (F   x) -> f x
-        (T d x) -> T d x
-
--- | The 'with' function will invoke the given function only if the predicate 'p'
--- applied to the test value 'a' evaluates to 'T'.
-with :: (Monad m, Predicate p a) => p -> a -> (TVal p -> m ()) -> m ()
-with p a f = case apply p a of
-    T _ x -> f x
-    _     -> return ()
diff --git a/src/Network/Wai/Routing/Predicate/Query.hs b/src/Network/Wai/Routing/Predicate/Query.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Predicate/Query.hs
+++ /dev/null
@@ -1,50 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
-module Network.Wai.Routing.Predicate.Query
-    ( Query
-    , HasQuery
-    , query
-    , hasQuery
-    ) where
-
-import Data.ByteString (ByteString)
-import Data.ByteString.From
-import Data.Monoid
-import Network.HTTP.Types.Status
-import Network.Wai.Routing.Error
-import Network.Wai.Routing.Internal
-import Network.Wai.Routing.Predicate.Predicate
-import Network.Wai.Routing.Request
-
-newtype Query a = Query ByteString
-
-query :: ByteString -> Query a
-query = Query
-{-# INLINABLE query #-}
-
-instance (FromByteString a) => Predicate (Query a) Req where
-    type FVal (Query a) = Error
-    type TVal (Query a) = a
-    apply (Query x)     =
-        let msg = "Missing query '" <> x <> "'." in
-        rqApply (lookupQuery x) readValues (err status400 msg)
-
-newtype HasQuery = HasQuery ByteString
-
-hasQuery :: ByteString -> HasQuery
-hasQuery = HasQuery
-{-# INLINABLE hasQuery #-}
-
-instance Predicate HasQuery Req where
-    type FVal HasQuery   = Error
-    type TVal HasQuery   = ()
-    apply (HasQuery x) r =
-        if null (lookupQuery x r)
-            then F (err status400 ("Missing query '" <> x <> "'."))
-            else T 0 ()
diff --git a/src/Network/Wai/Routing/Request.hs b/src/Network/Wai/Routing/Request.hs
--- a/src/Network/Wai/Routing/Request.hs
+++ b/src/Network/Wai/Routing/Request.hs
@@ -2,84 +2,48 @@
 -- License, v. 2.0. If a copy of the MPL was not distributed with this
 -- file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-{-# LANGUAGE OverloadedStrings     #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-
--- | A wrapped WAI 'Request' which holds additional data of interest
--- only to 'Predicate' authors.
 module Network.Wai.Routing.Request
-    ( Req
-    , GetRequest
-    , getRequest
-    , fromWaiRequest
-    , waiRequest
-    , method
-    , headers
-    , lookupHeader
+    ( RoutingReq
+    , HasCaptures (..)
+    , fromReq
     , lookupCapture
-    , lookupQuery
-    , lookupCookie
     ) where
 
 import Data.ByteString (ByteString)
-import Data.CaseInsensitive (mk)
-import Data.Maybe (mapMaybe)
-import Network.HTTP.Types
-import Network.Wai (Request)
-import Network.Wai.Routing.Predicate.Predicate
-import Web.Cookie
+import Network.Wai.Predicate.Request
 
-import qualified Network.Wai as Wai
+class HasCaptures a where
+     captures :: a -> [(ByteString, ByteString)]
 
-data Req = Req
-    { captures :: [(ByteString, ByteString)]
-    , request  :: Request
-    , cookies  :: Cookies
+data RoutingReq = RoutingReq
+    { _captures :: [(ByteString, ByteString)]
+    , _request  :: Req
     }
 
--- | A 'Predicate' which just returns the WAI 'Wai.Request'.
--- By including this predicate, handlers have easy access to
--- the complete request.
-data GetRequest a = GetRequest
+instance HasRequest RoutingReq where
+    getRequest = getRequest . _request
 
-getRequest :: GetRequest a
-getRequest = GetRequest
-{-# INLINABLE getRequest #-}
+instance HasMethod RoutingReq where
+    method = method . _request
 
-instance Predicate (GetRequest a) Req where
-    type FVal (GetRequest a) = a
-    type TVal (GetRequest a) = Request
-    apply GetRequest r       = T 0 (request r)
+instance HasHeaders RoutingReq where
+    headers = headers . _request
 
-fromWaiRequest :: [(ByteString, ByteString)] -> Request -> Req
-fromWaiRequest ca rq =
-    Req ca rq (concatMap parseCookies (getHeaders "Cookie" rq))
+instance HasCookies RoutingReq where
+    cookies = cookies . _request
 
-waiRequest :: Req -> Request
-waiRequest = request
+instance HasQuery RoutingReq where
+    queryItems = queryItems . _request
 
-headers :: Req -> RequestHeaders
-headers = Wai.requestHeaders . request
+instance HasPath RoutingReq where
+    segments = segments . _request
 
-method :: Req -> Method
-method = Wai.requestMethod . request
+instance HasCaptures RoutingReq where
+    captures = _captures
 
-lookupHeader :: ByteString -> Req -> [ByteString]
-lookupHeader name = getHeaders name . request
+fromReq :: [(ByteString, ByteString)] -> Req -> RoutingReq
+fromReq = RoutingReq
 
-lookupCapture :: ByteString -> Req -> [ByteString]
+lookupCapture :: (HasCaptures r) => ByteString -> r -> [ByteString]
 lookupCapture name = map snd . filter ((name ==) . fst) . captures
-
-lookupCookie :: ByteString -> Req -> [ByteString]
-lookupCookie name = map snd . filter ((name ==) . fst) . cookies
-
-lookupQuery :: ByteString -> Req -> [ByteString]
-lookupQuery name = mapMaybe snd
-                 . filter ((name ==) . fst)
-                 . Wai.queryString
-                 . request
-
-getHeaders :: ByteString -> Wai.Request -> [ByteString]
-getHeaders name = map snd . filter ((mk name ==) . fst) . Wai.requestHeaders
 
diff --git a/src/Network/Wai/Routing/Route.hs b/src/Network/Wai/Routing/Route.hs
--- a/src/Network/Wai/Routing/Route.hs
+++ b/src/Network/Wai/Routing/Route.hs
@@ -9,11 +9,11 @@
 
 module Network.Wai.Routing.Route
     ( Routes
-    , Renderer
-    , route
     , prepare
-    , renderer
+    , route
     , addRoute
+    , attach
+    , examine
     , get
     , Network.Wai.Routing.Route.head
     , post
@@ -22,8 +22,8 @@
     , trace
     , options
     , connect
-    , attach
-    , examine
+    , Renderer
+    , renderer
     ) where
 
 import Control.Applicative hiding (Const)
@@ -38,8 +38,8 @@
 import Data.Monoid
 import Network.HTTP.Types
 import Network.Wai (Request, Response, responseLBS, responseBuilder, rawPathInfo)
-import Network.Wai.Routing.Predicate
-import Network.Wai.Routing.Error
+import Network.Wai.Predicate
+import Network.Wai.Predicate.Request
 import Network.Wai.Routing.Request
 
 import qualified Data.ByteString.Char8  as C
@@ -55,15 +55,12 @@
     }
 
 data Handler m = Handler
-    { _delta   :: !Delta
+    { _delta   :: !Double
     , _handler :: m Response
     }
 
 data Pack m where
-    Pack :: (Predicate p Req, FVal p ~ Error)
-         => p
-         -> (TVal p -> m Response)
-         -> Pack m
+    Pack :: Predicate RoutingReq Error a -> (a -> m Response) -> Pack m
 
 -- | Function to turn an 'Error' value into a 'Lazy.ByteString'.
 -- Clients can provide their own renderer using 'renderer'.
@@ -71,7 +68,7 @@
 
 -- | Set a custom render function, i.e. a function to turn 'Error's into
 -- 'Lazy.ByteString's.
-renderer :: Renderer -> Routes m a ()
+renderer :: Renderer -> Routes a m ()
 renderer f = Routes . modify $ \s -> s { renderfn = f }
 
 -- | The Routes monad state type.
@@ -101,22 +98,22 @@
 
 -- | Add a route for some 'Method' and path (potentially with variable
 -- captures) and constrained by some 'Predicate'.
-addRoute :: (Monad m, Predicate p Req, FVal p ~ Error)
+addRoute :: Monad m
          => Method
-         -> ByteString             -- ^ path
-         -> (TVal p -> m Response) -- ^ handler
-         -> p                      -- ^ 'Predicate'
-         -> Routes a m ()
+         -> ByteString                   -- ^ path
+         -> (a -> m Response)            -- ^ handler
+         -> Predicate RoutingReq Error a -- ^ 'Predicate'
+         -> Routes b m ()
 addRoute m r x p = Routes . modify $ \s ->
     s { routes = Route m r Nothing (Pack p x) : routes s }
 
 -- | Specialisation of 'addRoute' for a specific HTTP 'Method'.
 get, head, post, put, delete, trace, options, connect ::
-    (Monad m, Predicate p Req, FVal p ~ Error)
-    => ByteString             -- ^ path
-    -> (TVal p -> m Response) -- ^ handler
-    -> p                      -- ^ 'Predicate'
-    -> Routes a m ()
+    Monad m
+    => ByteString                   -- ^ path
+    -> (a -> m Response)            -- ^ handler
+    -> Predicate RoutingReq Error a -- ^ 'Predicate'
+    -> Routes b m ()
 get     = addRoute (renderStdMethod GET)
 head    = addRoute (renderStdMethod HEAD)
 post    = addRoute (renderStdMethod POST)
@@ -140,17 +137,17 @@
 
 -- | A WAI 'Application' (generalised from 'IO' to 'Monad') which
 -- routes requests to handlers based on predicated route declarations.
-route :: Monad m => [(ByteString, Req -> m Response)] -> Request -> m Response
+route :: Monad m => [(ByteString, RoutingReq -> m Response)] -> Request -> m Response
 route rm rq = do
     let tr = Tree.fromList rm
     case Tree.lookup tr (Tree.segments $ rawPathInfo rq) of
-        Just (f, v) -> f (fromWaiRequest v rq)
+        Just (f, v) -> f (fromReq v (fromRequest rq))
         Nothing     -> return notFound
   where
     notFound = responseLBS status404 [] ""
 
 -- | Run the 'Routes' monad and return the handlers per path.
-prepare :: Monad m => Routes a m b -> [(ByteString, Req -> m Response)]
+prepare :: Monad m => Routes a m b -> [(ByteString, RoutingReq -> m Response)]
 prepare (Routes rr) =
     let s = execState rr zero in
     map (\g -> (_path (L.head g), select (renderfn s) g)) (normalise (routes s))
@@ -184,7 +181,7 @@
 -- (2) Evaluate 'Route' predicates.
 -- (3) Pick the first one which is 'Good', or else respond with status
 --     and message of the first one.
-select :: Monad m => Renderer -> [Route a m] -> Req -> m Response
+select :: Monad m => Renderer -> [Route a m] -> RoutingReq -> m Response
 select render rr req = do
     let ms = filter ((method req ==) . _method) rr
     if null ms
@@ -207,9 +204,9 @@
     evalSingle :: Monad m => [Either Error (Handler m)] -> Route a m -> [Either Error (Handler m)]
     evalSingle rs r =
         case _pred r of
-            Pack p h -> case apply p req of
-                F   m -> Left m : rs
-                T d v -> Right (Handler d (h v)) : rs
+            Pack p h -> case p req of
+                Fail   m -> Left m : rs
+                Okay d v -> Right (Handler d (h v)) : rs
 
     closest :: Monad m => [Handler m] -> m Response
     closest hh = case map _handler . sortBy (compare `on` _delta) $ hh of
@@ -218,3 +215,4 @@
 
 respond :: Renderer -> Error -> ResponseHeaders -> Response
 respond f e h = responseLBS (status e) h (fromMaybe mempty (f e))
+
diff --git a/src/Network/Wai/Routing/Tutorial.hs b/src/Network/Wai/Routing/Tutorial.hs
deleted file mode 100644
--- a/src/Network/Wai/Routing/Tutorial.hs
+++ /dev/null
@@ -1,176 +0,0 @@
--- This Source Code Form is subject to the terms of the Mozilla Public
--- License, v. 2.0. If a copy of the MPL was not distributed with this
--- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-module Network.Wai.Routing.Tutorial
-  ( -- * Motivation
-    -- $motivation
-
-    -- * Introduction
-    -- $introduction
-
-    -- * Example Predicate
-    -- $example
-
-    -- * Routes
-    -- $routes
-  )
-where
-
-{- $motivation
-
-The purpose of the @wai-routing@ package is to facilitate the
-convenient definition of safe WAI 'Application's. Here safety
-means that a handler can declare all pre-conditions which must be
-fulfilled such that the handler can produce a successful response (excluding
-the request body). It is then statically guaranteed that the handler will not be
-invoked if any of these pre-conditions fails.
-
--}
-
-{- $introduction
-
-The @wai-routing@ package defines a 'Network.Wai.Routing.Predicate.Predicate.Boolean' type
-which carries \-\- in addition to actual truth values @T@ and @F@ \-\- meta-data for each case:
-
-@
-data Boolean f t
-    = F f
-    | T Delta t
-    deriving (Eq, Show)
-@
-
-'Network.Wai.Routing.Predicate.Predicate.Delta' can in most instances be ignored, i.e. set to 0.
-Its purpose is as a measure of distance for those predicates which evaluate
-to @T@ but some may be \"closer\" in some way than others. An
-example is for instance HTTP content-negotiations (cf.
-'Network.Wai.Routing.Predicate.Accept.Accept')
-
-In addition there is a type-class 'Network.Wai.Routing.Predicate.Predicate.Predicate' defined which
-contains an evaluation function 'Network.Wai.Routing.Predicate.Predicate.apply', where the
-predicate instance is applied to some value, yielding @T@ or @F@.
-
-@
-class Predicate p a where
-    type FVal p
-    type TVal p
-    apply :: p -> a -> Boolean (FVal p) (TVal p)
-@
-
-All predicates are instances of this type-class, which does not
-specify the type against which the predicate is evaluated, nor the types
-of actual meta-data for the true/false case of the Boolean returned.
-WAI related predicates are defined against 'Network.Wai.Routing.Request.Req'
-which holds a regular 'Network.Wai.Request' and path capture variables.
-In case predicates fail, they return a status code and an optional message.
-
-Besides these type definitions, there are some ways to connect two
-predicates to form a new one as the logical @OR@ or the
-logical @AND@ of its parts. These are:
-
-  * 'Network.Wai.Routing.Predicate.Predicate.:|:' and 'Network.Wai.Routing.Predicate.Predicate.:||:' as logical @OR@s
-
-  * 'Network.Wai.Routing.Predicate.Predicate.:&:' as logical @AND@
-
-In addition to evaluating to @T@ or @F@ depending on the truth values of
-its parts, these connectives also propagate the meta-data and @Delta@
-appropriately.
-
-If @:&:@ evaluates to @F@ it has to combine the meta-data of both predicates,
-and it uses the product type 'Network.Wai.Routing.Predicate.Predicate.:::' for this.
-This type also has a data constructor with the same symbol, so one can
-combine many predicates without having to nest the meta-data pairs.
-
-In the @OR@ case, the two predicates have potentially meta-data of
-different types, so we use a sum type 'Either' whenever we combine
-two predicates with @:||:@. For convenience a type-alias
-@:+:@ is defined for 'Either', which allows simple infix
-notation. However, for the common case where both predicates have
-meta-data of the same type, there is often no need to distinguish which
-@OR@-branch was true. In this case, the @:|:@ combinator can be used.
-
-Finally there are 'Network.Wai.Routing.Predicate.Predicate.Const' and
-'Network.Wai.Routing.Predicate.Predicate.Fail' to always evaluate to @T@ or @F@
-respectively.
-
-As an example of how these operators are used, see below in section \"Routes\".
--}
-
-{- $example
-
-@
-newtype Query = Query ByteString
-
-instance Predicate Query Req where
-    type FVal Query = Error
-    type TVal Query = ByteString
-    apply (Query x) r =
-        case lookupQuery x r of
-            []    -> F (Error 400 (Just $ \"Expected parameter '\" \<\> x \<\> \"'.\"))
-            (v:_) -> T [] v
-@
-
-This is a simple example looking for the existence of a 'Req' query
-parameter with the given name. In the success case, the query value is
-returned.
-
-As mentioned before, WAI predicates usually fix the type @a@ from
-@Predicate@ above to 'Network.Wai.Routing.Request.Req'. The associated
-types 'Network.Wai.Routing.Predicate.Predicate.FVal' and
-'Network.Wai.Routing.Predicate.Predicate.TVal' denote the meta-data
-types of the predicate. In this example, the meta-date type is
-'Data.ByteString.ByteString'. The @F@-case is 'Network.Wai.Routing.Error.Error'
-which contains a status code and an optional message.
-
--}
-
-{- $routes
-
-So how are @Predicate@s used in an application?
-One way is to just evaluate them against a given request, e.g.
-
-@
-someHandler :: Application
-someHandler r =
-    case apply (accept :&: query \"baz\") (fromWaiRequest [] r) of
-        T ((_ :: Media \"text\" \"plain\") ::: bazValue) -> ...
-        F (Just (Error st msg))                      -> ...
-        F Nothing                                    -> ...
-@
-
-This however requires the manual construction of a 'Network.Wai.Routing.Request.Req' and
-for brevity we did not provide the list of captured path parameters.
-The intended application of @wai-routing@ is to declare route definitions with the
-'Network.Wai.Routing.Route.Routes' monad which can be turned into a WAI @Application@
-generalised from IO to arbitrary @Monad@s through 'Network.Wai.Routing.Route.route'.
-This application will at runtime select the actual handler to invoke (using the @wai-route@
-library).
-
-@
-sitemap :: Routes ()
-sitemap = do
-    get  \"\/a\" handlerA $ accept :&: (query \"name\" :|: query \"nick\") :&: query \"foo\"
-    get  \"\/b\" handlerB $ accept :&: (query \"name\" :||: query \"nick\") :&: query \"foo\"
-    get  \"\/c\" handlerC $ failure (Error 410 (Just \"Gone.\"))
-    post \"\/d\" handlerD $ accept
-    post \"\/e\" handlerE $ accept
-@
-
-Handler definitions encode their pre-conditions in their type-signature:
-
-@
-handlerA :: Media \"application\" \"json\" ::: ByteString ::: ByteString -> IO Response
-handlerB :: Media \"text\" \"plain\" ::: (ByteString :+: ByteString) ::: ByteString -> IO Response
-handlerC :: Media \"application\" \"json\" ::: Char -> IO Response
-handlerD :: Media \"application\" \"x-protobuf\" -> IO Response
-handlerE :: Media \"application\" \"xml\" -> IO Response
-@
-
-The type-declaration of a handler has to match the corresponding predicate,
-i.e. the type of the predicate's @T@ meta-data value.
-
-One thing to note is that @Fail@ works with
-all @T@ meta-data types which is safe because the handler is never
-invoked, or @Fail@ is used in some logical disjunction.
--}
-
diff --git a/test/TestSuite.hs b/test/TestSuite.hs
--- a/test/TestSuite.hs
+++ b/test/TestSuite.hs
@@ -1,13 +1,7 @@
 module Main where
 
 import Test.Tasty
-import qualified Tests.Data.Predicate as Predicate
-import qualified Tests.Wai.Predicate  as WaiPredicate
-import qualified Tests.Wai.Route      as WaiRoute
+import qualified Tests.Wai.Route as WaiRoute
 
 main :: IO ()
-main = defaultMain $ testGroup "Tests"
-    [ Predicate.tests
-    , WaiPredicate.tests
-    , WaiRoute.tests
-    ]
+main = defaultMain $ testGroup "Tests" [ WaiRoute.tests ]
diff --git a/test/Tests/Data/Predicate.hs b/test/Tests/Data/Predicate.hs
deleted file mode 100644
--- a/test/Tests/Data/Predicate.hs
+++ /dev/null
@@ -1,54 +0,0 @@
-{-# LANGUAGE FlexibleInstances     #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies          #-}
-{-# OPTIONS_GHC -fno-warn-orphans  #-}
-module Tests.Data.Predicate (tests) where
-
-import Control.Applicative hiding (Const, empty)
-import Network.Wai.Routing.Predicate.Predicate
-import Test.QuickCheck
-import Test.Tasty
-import Test.Tasty.QuickCheck
-
-tests :: TestTree
-tests = testGroup "Data.Predicate"
-    [ testProperty "(:&:)" testAnd
-    , testProperty "(:||:)" testOr
-    , testProperty "(:|:)" testOr'
-    ]
-
-testAnd :: Rand -> Rand -> Bool
-testAnd a@(Rand (T d x)) b@(Rand (T w y)) = apply (a :&: b) () == T (d + w) (x ::: y)
-testAnd a@(Rand (T _ _)) b@(Rand (F   y)) = apply (a :&: b) () == F y
-testAnd a@(Rand (F   x)) b@(Rand (T _ _)) = apply (a :&: b) () == F x
-testAnd a@(Rand (F   x)) b@(Rand (F   _)) = apply (a :&: b) () == F x
-
-testOr :: Rand -> Rand -> Bool
-testOr a@(Rand (T d x)) b@(Rand (T e y)) = apply (a :||: b) () == if d <= e then T d (Left x) else T e (Right y)
-testOr a@(Rand (T d x)) b@(Rand (F   _)) = apply (a :||: b) () == T d (Left x)
-testOr a@(Rand (F   _)) b@(Rand (T d y)) = apply (a :||: b) () == T d (Right y)
-testOr a@(Rand (F   _)) b@(Rand (F   y)) = apply (a :||: b) () == F y
-
-testOr' :: Rand -> Rand -> Bool
-testOr' a@(Rand (T d x)) b@(Rand (T e y)) = apply (a :|: b) () == if d <= e then T d x else T e y
-testOr' a@(Rand (T d x)) b@(Rand (F   _)) = apply (a :|: b) () == T d x
-testOr' a@(Rand (F   _)) b@(Rand (T d y)) = apply (a :|: b) () == T d y
-testOr' a@(Rand (F   _)) b@(Rand (F   y)) = apply (a :|: b) () == F y
-
-newtype Rand = Rand
-  { _rand :: Boolean Int Char
-  } deriving Show
-
-instance Predicate Rand a where
-    type FVal Rand   = Int
-    type TVal Rand   = Char
-    apply (Rand x) _ = x
-
-instance Arbitrary (Boolean Int Char) where
-    arbitrary =
-        oneof [ T <$> (arbitrary :: Gen Delta) <*> (arbitrary :: Gen Char)
-              , F <$> (arbitrary :: Gen Int)
-              ]
-
-instance Arbitrary Rand where
-    arbitrary = Rand <$> (arbitrary :: Gen (Boolean Int Char))
diff --git a/test/Tests/Wai/Predicate.hs b/test/Tests/Wai/Predicate.hs
deleted file mode 100644
--- a/test/Tests/Wai/Predicate.hs
+++ /dev/null
@@ -1,76 +0,0 @@
-{-# LANGUAGE DataKinds         #-}
-{-# LANGUAGE FlexibleContexts  #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeFamilies      #-}
-
-module Tests.Wai.Predicate (tests) where
-
-import Data.ByteString (ByteString)
-import Network.HTTP.Types.Status
-import Network.Wai.Routing
-import Test.Tasty
-import Test.Tasty.HUnit
-import Tests.Wai.Util
-
-tests :: TestTree
-tests = testGroup "Wai.Predicate"
-    [ testCase "Accept application/json" testAcceptJson
-    , testCase "Accept application/thrift " testAcceptThrift
-    , testCase "Accept application/*" testAcceptAll
-    , testCase "Content-Type text/plain" testContentTypePlain
-    , testCase "Content-Type text/*" testContentTypeAll
-    , testCase "Query" testQuery
-    , testCase "QueryOpt" testQueryOpt
-    ]
-
-testAcceptJson :: IO ()
-testAcceptJson = do
-    let rq0 = fromWaiRequest [] . json $ request "/"
-    T 0 (Media "application" "json" 1.0 []) @=? apply (accept :: Accept "application" "json") rq0
-
-    let rq1 = fromWaiRequest [] . withHeader "Accept" "foo/bar" $ request "/"
-    F (err status406 ("Expected 'Accept: application/json'.")) @=? apply (accept :: Accept "application" "json") rq1
-
-testAcceptThrift :: IO ()
-testAcceptThrift = do
-    let rq0 = fromWaiRequest [] . withHeader "Accept" "application/x-thrift" $ request "/"
-    T 0 (Media "application" "x-thrift" 1.0 []) @=? apply (accept :: Accept "application" "x-thrift") rq0
-
-    let rq1 = fromWaiRequest [] . json $ request "/"
-    F (err status406 ("Expected 'Accept: application/x-thrift'.")) @=? apply (accept :: Accept "application" "x-thrift") rq1
-
-testAcceptAll :: IO ()
-testAcceptAll = do
-    let rq0 = fromWaiRequest [] . withHeader "Accept" "application/*" $ request "/"
-    T 0 (Media "application" "*"    1.0 []) @=? apply (accept :: Accept "application" "*") rq0
-    T 0 (Media "application" "json" 1.0 []) @=? apply (accept :: Accept "application" "json") rq0
-
-testContentTypePlain :: IO ()
-testContentTypePlain = do
-    let rq0 = fromWaiRequest [] . withHeader "Content-Type" "text/plain" $ request "/"
-    T 0 (Media "text" "plain" 1.0 []) @=? apply (contentType :: ContentType "text" "plain") rq0
-
-    let rq1 = fromWaiRequest [] . withHeader "Content-Type" "text/html" $ request "/"
-    F (err status415 ("Expected 'Content-Type: text/plain'.")) @=? apply (contentType :: ContentType "text" "plain") rq1
-
-testContentTypeAll :: IO ()
-testContentTypeAll = do
-    let rq0 = fromWaiRequest [] . withHeader "Content-Type" "text/plain" $ request "/"
-    T 0.5 (Media "text" "plain" 0.5 []) @=? apply (contentType :: ContentType "text" "*") rq0
-
-testQuery :: IO ()
-testQuery = do
-    let rq0 = fromWaiRequest [] . withQuery "x" "y" . withQuery "x" "z" $ request "/"
-    T 0 "y" @=? apply (query "x" :: Query ByteString) rq0
-
-    let rq1 = fromWaiRequest [] $ request "/"
-    F (err status400 ("Missing query 'x'.")) @=? apply (query "x" :: Query ByteString) rq1
-
-testQueryOpt :: IO ()
-testQueryOpt = do
-    let rq0 = fromWaiRequest [] . withQuery "x" "y" . withQuery "x" "z" $ request "/"
-    T 0 (Just "y") @=? apply (opt (query "x" :: Query ByteString)) rq0
-
-    let rq1 = fromWaiRequest [] $ request "/"
-    T 0 Nothing @=? apply (opt (query "x" :: Query ByteString)) rq1
-
diff --git a/test/Tests/Wai/Route.hs b/test/Tests/Wai/Route.hs
--- a/test/Tests/Wai/Route.hs
+++ b/test/Tests/Wai/Route.hs
@@ -9,7 +9,10 @@
 import Data.String
 import Network.HTTP.Types
 import Network.Wai
+import Network.Wai.Predicate
+import Network.Wai.Predicate.Request
 import Network.Wai.Routing
+import Network.Wai.Routing.Request
 import Test.HUnit hiding (Test)
 import Test.Tasty
 import Test.Tasty.HUnit
@@ -42,7 +45,7 @@
 sitemap :: Routes Int IO ()
 sitemap = do
     get "/a" handlerA $
-        accept :&: (query "name" :|: query "nick") :&: query "foo"
+        accept "application" "json" .&. (query "name" .|. query "nick") .&. query "foo"
 
     attach 0
 
@@ -62,7 +65,7 @@
     attach 3
 
     get "/e" handlerE $
-        def 0 (hdr "foo")
+        def 0 (header "foo")
 
     attach 4
 
@@ -76,7 +79,7 @@
     attach 6
 
     get "/h" handlerH $
-        cookie "user" :&: cookie "age"
+        cookie "user" .&. cookie "age"
 
     attach 7
 
@@ -225,8 +228,8 @@
 
 sitemapMedia :: Routes a IO ()
 sitemapMedia = do
-    get "/media" handlerJson   accept
-    get "/media" handlerThrift accept
+    get "/media" handlerJson   $ accept "application" "json"
+    get "/media" handlerThrift $ accept "application" "x-thrift"
 
 handlerJson :: Media "application" "json" -> IO Response
 handlerJson _ = writeText "application/json"
@@ -234,8 +237,8 @@
 handlerThrift :: Media "application" "x-thrift" -> IO Response
 handlerThrift _ = writeText "application/x-thrift"
 
-expectMedia :: ByteString -> ByteString -> (Req -> IO Response) -> Assertion
+expectMedia :: ByteString -> ByteString -> (RoutingReq -> IO Response) -> Assertion
 expectMedia h res m = do
     let rq = defaultRequest { rawPathInfo = "/media" }
-    rs <- m . fromWaiRequest [] . withHeader "Accept" h $ rq
+    rs <- m . fromReq [] . fromRequest . withHeader "Accept" h $ rq
     Lazy.fromStrict res @=? responseBody rs
diff --git a/wai-routing.cabal b/wai-routing.cabal
--- a/wai-routing.cabal
+++ b/wai-routing.cabal
@@ -1,5 +1,5 @@
 name:                wai-routing
-version:             0.3.1
+version:             0.4
 synopsis:            Declarative routing for WAI.
 license:             OtherLicense
 license-file:        LICENSE
@@ -23,14 +23,38 @@
     which operate on WAI requests and have to be true or else the
     handler will not be called.
     .
-    For details have a look at the haddock documentation of
-    @Network.Wai.Routing.Tutorial@ or the @examples@ folder in the
-    source distribution.
-    .
-    This library is a port of @snap-predicates@ which provides
-    similar functionality for the snap-framework.
+    Example:
     .
-    The routing tree construction is implemented using @wai-route@.
+    >import Data.ByteString (ByteString)
+    >import Data.Text (Text)
+    >import Network.Wai
+    >import Network.Wai.Predicate
+    >import Network.Wai.Routing
+    >import Network.Wai.Handler.Warp
+    >
+    >main :: IO ()
+    >main = run 8080 (route (prepare start))
+    >
+    >start :: Monad m => Routes a m ()
+    >start = do
+    >    get "/user/:name" fetchUser $
+    >        capture "name"
+    >
+    >    get "/user/find" findUser $
+    >        query "byName" ||| query "byId"
+    >
+    >    delete "/user/:name" rmUser $
+    >        capture "name" .&. opt (cookie "foo")
+    >
+    >fetchUser :: Monad m => Text -> m Response
+    >fetchUser name = ...
+    >
+    >findUser :: Monad m => Either ByteString Word64 -> m Response
+    >findUser (Left  name)  = ...
+    >findUser (Right ident) = ...
+    >
+    >rmUser :: Monad m => Text ::: Maybe Int -> m Response
+    >rmUser (name ::: foo)  = ...
 
 source-repository head
     type:             git
@@ -44,36 +68,21 @@
 
     exposed-modules:
         Network.Wai.Routing
-        Network.Wai.Routing.Error
-        Network.Wai.Routing.MediaType
         Network.Wai.Routing.Request
         Network.Wai.Routing.Route
-        Network.Wai.Routing.Tutorial
         Network.Wai.Routing.Predicate
-        Network.Wai.Routing.Predicate.Predicate
-        Network.Wai.Routing.Predicate.Accept
-        Network.Wai.Routing.Predicate.Capture
-        Network.Wai.Routing.Predicate.Content
-        Network.Wai.Routing.Predicate.Cookie
-        Network.Wai.Routing.Predicate.Header
-        Network.Wai.Routing.Predicate.Param
-        Network.Wai.Routing.Predicate.Query
 
-    other-modules:
-        Network.Wai.Routing.Internal
-        Network.Wai.Routing.Parser.MediaType
-        Network.Wai.Routing.Parser.Shared
-
     build-depends:
         attoparsec       >= 0.10  && < 0.12
       , base             == 4.*
       , bytestring       >= 0.9   && < 0.11
-      , bytestring-from  == 0.1.*
+      , bytestring-from  == 0.2.*
       , cookie           == 0.4.*
       , case-insensitive == 1.1.*
       , http-types       == 0.8.*
       , transformers     == 0.3.*
       , wai              == 2.0.*
+      , wai-predicates   == 0.2.*
       , wai-route        == 0.1.*
 
 test-suite wai-routing-tests
@@ -85,21 +94,20 @@
     ghc-prof-options: -prof -auto-all
 
     other-modules:
-        Tests.Data.Predicate
-        Tests.Wai.Predicate
         Tests.Wai.Route
         Tests.Wai.Util
 
     build-depends:
-        base                 == 4.*
-      , blaze-builder        == 0.3.*
+        base             == 4.*
+      , blaze-builder    == 0.3.*
       , bytestring
       , case-insensitive
       , http-types
-      , HUnit                >= 1.2
-      , QuickCheck           >= 2.3
-      , tasty                >= 0.3
-      , tasty-hunit          >= 0.2
-      , tasty-quickcheck     >= 0.3
+      , HUnit            >= 1.2
+      , QuickCheck       >= 2.3
+      , tasty            >= 0.3
+      , tasty-hunit      >= 0.2
+      , tasty-quickcheck >= 0.3
       , wai
+      , wai-predicates   == 0.2.*
       , wai-routing
