diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014 - 2015 Alexander Thiemann <mail@athiemann.net>
+Copyright (c) 2014 - 2021 Alexander Thiemann <mail@athiemann.net>
 Copyright (c) 2014 - 2015 Tim Baumann <tim@timbaumann.info>
 
 Permission is hereby granted, free of charge, to any person obtaining
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,3 @@
 import Distribution.Simple
+
 main = defaultMain
diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs
--- a/benchmarks/Benchmarks.hs
+++ b/benchmarks/Benchmarks.hs
@@ -1,15 +1,15 @@
-{-# LANGUAGE OverloadedStrings, DataKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Main where
 
-import Web.Routing.Combinators
-import Web.Routing.SafeRouting
-
 import Criterion.Main
+import Data.List (foldl', permutations)
+import Data.Maybe (fromMaybe, listToMaybe)
 import qualified Data.Text as T
-import Data.List (permutations, foldl')
 import System.Random (mkStdGen, randomRs)
-import Data.Maybe (listToMaybe, fromMaybe)
+import Web.Routing.Combinators
+import Web.Routing.SafeRouting
 
 buildPath :: [T.Text] -> PathInternal '[]
 buildPath = toInternalPath . static . T.unpack . T.intercalate "/"
@@ -25,16 +25,17 @@
 benchmarks :: [Benchmark]
 benchmarks =
   [ env setupSafeMap $ \ ~(safeMap, routes') ->
-    bgroup "SafeRouting"
-    [ bench "static-lookup" $ whnf (lookupPathMapM routes') safeMap
-    ]
+      bgroup
+        "SafeRouting"
+        [ bench "static-lookup" $ whnf (lookupPathMapM routes') safeMap
+        ]
   ]
   where
     strlen = 10
     seglen = 5
     num = 10
     routes = rndRoutes strlen seglen num
-    routesList = zip routes [1..]
+    routesList = zip routes [1 ..]
     setupSafeMap = return (buildPathMap routesList, routes)
 
 main :: IO ()
@@ -43,16 +44,22 @@
 chunks :: Int -> [a] -> [[a]]
 chunks n xs =
   let (ys, xs') = splitAt n xs
-  in ys : chunks n xs'
+   in ys : chunks n xs'
 
 -- | Generate a number of paths consisting of a fixed number of fixed length
 -- strings ("path segments") where the content of the segments are letters in
 -- random order. Contains all permutations with the path.
 rndRoutes ::
-     Int -- ^ Length of each string
-  -> Int -- ^ Number of segments
-  -> Int -- ^ Number of routes
-  -> [[T.Text]]
+  -- | Length of each string
+  Int ->
+  -- | Number of segments
+  Int ->
+  -- | Number of routes
+  Int ->
+  [[T.Text]]
 rndRoutes strlen seglen num =
-  take num $ concatMap permutations $ chunks seglen $ map T.pack $
-    chunks strlen $ randomRs ('a', 'z') $ mkStdGen 1234
+  take num $
+    concatMap permutations $
+      chunks seglen $
+        map T.pack $
+          chunks strlen $ randomRs ('a', 'z') $ mkStdGen 1234
diff --git a/reroute.cabal b/reroute.cabal
--- a/reroute.cabal
+++ b/reroute.cabal
@@ -1,5 +1,5 @@
 name:                reroute
-version:             0.6.0.0
+version:             0.7.0.0
 synopsis:            abstract implementation of typed and untyped web routing
 description:         abstraction over how urls with/without parameters are mapped to their corresponding handlers
 homepage:            http://github.com/agrafix/Spock
@@ -7,11 +7,11 @@
 license-file:        LICENSE
 author:              Alexander Thiemann <mail@athiemann.net>, Tim Baumann <tim@timbaumann.info>
 maintainer:          Alexander Thiemann <mail@athiemann.net>
-copyright:           (c) 2014 - 2020 Alexander Thiemann <mail@athiemann.net>, Tim Baumann <tim@timbaumann.info>
+copyright:           (c) 2014 - 2021 Alexander Thiemann <mail@athiemann.net>, Tim Baumann <tim@timbaumann.info>
 category:            Web
 build-type:          Simple
-cabal-version:       >=2.0
-tested-with:         GHC==8.8.4
+cabal-version:       2.0
+tested-with:         GHC==8.8.4, GHC==9.2.2
 
 extra-source-files:
     README.md
diff --git a/src/Data/PolyMap.hs b/src/Data/PolyMap.hs
--- a/src/Data/PolyMap.hs
+++ b/src/Data/PolyMap.hs
@@ -1,19 +1,31 @@
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE GADTs #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 module Data.PolyMap
-  ( PolyMap, empty, rnfHelper
-  , lookup, lookupApply, lookupApplyAll, lookupConcat
-  , alter,  updateAll, insertWith
-  , unionWith, union
-  , zipWith', zipWith, zip, ap
-  ) where
+  ( PolyMap,
+    empty,
+    rnfHelper,
+    lookup,
+    lookupApply,
+    lookupApplyAll,
+    lookupConcat,
+    alter,
+    updateAll,
+    insertWith,
+    unionWith,
+    union,
+    zipWith',
+    zipWith,
+    zip,
+    ap,
+  )
+where
 
 import Data.Typeable
-import Prelude hiding (lookup, zipWith, zip)
 #if MIN_VERSION_base(4,11,0)
 import Control.Applicative (Alternative ((<|>)), liftA2)
 #elif MIN_VERSION_base(4,9,0)
@@ -26,6 +38,7 @@
 import Data.Monoid (Monoid (..))
 #endif
 import GHC.Exts (Constraint)
+import Prelude hiding (lookup, zip, zipWith)
 
 data PolyMap (c :: * -> Constraint) (f :: * -> *) (a :: *) where
   PMNil :: PolyMap c f a
@@ -50,9 +63,9 @@
 rnfHelper h (PMCons v pm) = h v `seq` rnfHelper h pm
 
 lookup ::
-  Typeable p
-  => PolyMap c f a
-  -> Maybe (f (p -> a))
+  Typeable p =>
+  PolyMap c f a ->
+  Maybe (f (p -> a))
 lookup PMNil = Nothing
 lookup (PMCons w polyMap') =
   case gcast1 w of
@@ -60,10 +73,10 @@
     Nothing -> lookup polyMap'
 
 lookupApply ::
-  (Typeable p, Functor f)
-  => p
-  -> PolyMap c f a
-  -> Maybe (f a)
+  (Typeable p, Functor f) =>
+  p ->
+  PolyMap c f a ->
+  Maybe (f a)
 lookupApply _ PMNil = Nothing
 lookupApply p (PMCons w polyMap') =
   case gcast1 w of
@@ -71,47 +84,46 @@
     Nothing -> lookupApply p polyMap'
 
 lookupApplyAll ::
-  Functor f
-  => (forall p. c p => Maybe p)
-  -> PolyMap c f a
-  -> [f a]
+  Functor f =>
+  (forall p. c p => Maybe p) ->
+  PolyMap c f a ->
+  [f a]
 lookupApplyAll maybeGet polyMap =
   case polyMap of
     PMNil -> []
     PMCons w polyMap' ->
       let rest = lookupApplyAll maybeGet polyMap'
-      in case maybeGet of
-           Nothing -> rest
-           Just p  -> (fmap ($ p) w) : rest
+       in case maybeGet of
+            Nothing -> rest
+            Just p -> (fmap ($ p) w) : rest
 
 lookupConcat ::
-  (Monoid m, Functor f)
-  => (forall p. c p => Maybe p)
-  -> (forall p. c p => p -> f (p -> a) -> m)
-  -> PolyMap c f a
-  -> m
+  (Monoid m, Functor f) =>
+  (forall p. c p => Maybe p) ->
+  (forall p. c p => p -> f (p -> a) -> m) ->
+  PolyMap c f a ->
+  m
 lookupConcat maybeGet comp polyMap =
   case polyMap of
     PMNil -> mempty
     PMCons w polyMap' ->
       let rest = lookupConcat maybeGet comp polyMap'
-      in case maybeGet of
-           Nothing -> rest
-           Just p  -> comp p w `mappend` rest
-
+       in case maybeGet of
+            Nothing -> rest
+            Just p -> comp p w `mappend` rest
 
 maybeInsertHere ::
-  (c p, Typeable p)
-  => Maybe (f (p -> a))
-  -> PolyMap c f a
-  -> PolyMap c f a
+  (c p, Typeable p) =>
+  Maybe (f (p -> a)) ->
+  PolyMap c f a ->
+  PolyMap c f a
 maybeInsertHere = maybe id PMCons
 
 alter ::
-  (Typeable p, c p)
-  => (Maybe (f (p -> a)) -> Maybe (f (p -> a)))
-  -> PolyMap c f a
-  -> PolyMap c f a
+  (Typeable p, c p) =>
+  (Maybe (f (p -> a)) -> Maybe (f (p -> a))) ->
+  PolyMap c f a ->
+  PolyMap c f a
 alter (g :: Maybe (f (p -> a)) -> Maybe (f (p -> a))) polyMap =
   case polyMap of
     PMNil -> insertHere PMNil
@@ -123,53 +135,56 @@
             Nothing -> polyMap'
         Nothing ->
           if typeOf (undefined :: p) < typeOf (undefined :: q)
-          then insertHere polyMap
-          else PMCons w (alter g polyMap')
-  where insertHere = maybe id PMCons (g Nothing)
+            then insertHere polyMap
+            else PMCons w (alter g polyMap')
+  where
+    insertHere = maybe id PMCons (g Nothing)
 
 updateAll ::
-     (forall p. c p => f (p -> a) -> g (p -> b))
-  -> PolyMap c f a
-  -> PolyMap c g b
+  (forall p. c p => f (p -> a) -> g (p -> b)) ->
+  PolyMap c f a ->
+  PolyMap c g b
 updateAll _ PMNil = PMNil
 updateAll f (PMCons v pm) = PMCons (f v) (updateAll f pm)
 
 insertWith ::
-  (Typeable p, c p)
-  => (f (p -> a) -> f (p -> a) -> f (p -> a))
-  -> f (p -> a)
-  -> PolyMap c f a
-  -> PolyMap c f a
+  (Typeable p, c p) =>
+  (f (p -> a) -> f (p -> a) -> f (p -> a)) ->
+  f (p -> a) ->
+  PolyMap c f a ->
+  PolyMap c f a
 insertWith combine val = alter (Just . maybe val (combine val))
 
 unionWith ::
-     (forall p. c p => f (p -> a) -> f (p -> a) -> f (p -> a))
-  -> PolyMap c f a
-  -> PolyMap c f a
-  -> PolyMap c f a
+  (forall p. c p => f (p -> a) -> f (p -> a) -> f (p -> a)) ->
+  PolyMap c f a ->
+  PolyMap c f a ->
+  PolyMap c f a
 unionWith _ PMNil pm2 = pm2
 unionWith _ pm1 PMNil = pm1
-unionWith combine pm1@(PMCons (v :: f (p -> a)) pm1')
-                  pm2@(PMCons (w :: f (q -> a)) pm2') =
-  case gcast1 v of
-    Just v' -> PMCons (combine v' w) (unionWith combine pm1' pm2')
-    Nothing ->
+unionWith
+  combine
+  pm1@(PMCons (v :: f (p -> a)) pm1')
+  pm2@(PMCons (w :: f (q -> a)) pm2') =
+    case gcast1 v of
+      Just v' -> PMCons (combine v' w) (unionWith combine pm1' pm2')
+      Nothing ->
         if typeOf (undefined :: p) < typeOf (undefined :: q)
-        then PMCons v (unionWith combine pm1' pm2)
-        else PMCons w (unionWith combine pm1 pm2')
+          then PMCons v (unionWith combine pm1' pm2)
+          else PMCons w (unionWith combine pm1 pm2')
 
 union ::
-  Alternative f
-  => PolyMap c f a
-  -> PolyMap c f a
-  -> PolyMap c f a
+  Alternative f =>
+  PolyMap c f a ->
+  PolyMap c f a ->
+  PolyMap c f a
 union = unionWith (<|>)
 
 zipWith' ::
-     (forall p. c p => Maybe (f (p -> a)) -> Maybe (f (p -> b)) -> Maybe (f (p -> d)))
-  -> PolyMap c f a
-  -> PolyMap c f b
-  -> PolyMap c f d
+  (forall p. c p => Maybe (f (p -> a)) -> Maybe (f (p -> b)) -> Maybe (f (p -> d))) ->
+  PolyMap c f a ->
+  PolyMap c f b ->
+  PolyMap c f d
 zipWith' f = go
   where
     go PMNil PMNil = PMNil
@@ -180,27 +195,27 @@
         Just v' -> maybeInsertHere (f (Just v') (Just w)) (go pm1 pm2)
         Nothing ->
           if typeOf (undefined :: p) < typeOf (undefined :: q)
-          then maybeInsertHere (f (Just v) Nothing) (go pm1' pm2)
-          else maybeInsertHere (f Nothing (Just w)) (go pm1 pm2')
+            then maybeInsertHere (f (Just v) Nothing) (go pm1' pm2)
+            else maybeInsertHere (f Nothing (Just w)) (go pm1 pm2')
 
 zipWith ::
-  Applicative f
-  => (a -> b -> d)
-  -> PolyMap c f a
-  -> PolyMap c f b
-  -> PolyMap c f d
+  Applicative f =>
+  (a -> b -> d) ->
+  PolyMap c f a ->
+  PolyMap c f b ->
+  PolyMap c f d
 zipWith f = zipWith' $ liftA2 $ liftA2 $ \a b p -> f (a p) (b p)
 
 zip ::
-  Applicative f
-  => PolyMap c f a
-  -> PolyMap c f b
-  -> PolyMap c f (a, b)
+  Applicative f =>
+  PolyMap c f a ->
+  PolyMap c f b ->
+  PolyMap c f (a, b)
 zip = zipWith (,)
 
 ap ::
-  Applicative f
-  => PolyMap c f (a -> b)
-  -> PolyMap c f a
-  -> PolyMap c f b
+  Applicative f =>
+  PolyMap c f (a -> b) ->
+  PolyMap c f a ->
+  PolyMap c f b
 ap = zipWith ($)
diff --git a/src/Web/Routing/Combinators.hs b/src/Web/Routing/Combinators.hs
--- a/src/Web/Routing/Combinators.hs
+++ b/src/Web/Routing/Combinators.hs
@@ -5,14 +5,14 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
+
 module Web.Routing.Combinators where
 
 import Data.HVect
 import Data.String
+import qualified Data.Text as T
 import Data.Typeable (Typeable)
 import Web.HttpApiData
-import qualified Data.Text as T
-
 import Web.Routing.SafeRouting
 
 data PathState = Open | Closed
@@ -31,6 +31,20 @@
 
 type Var a = Path (a ': '[]) 'Open
 
+-- | A variant of 'Either' with a 'FromHttpApiData' definition that tries both branches without a prefix.
+-- Useful to define routes with 'var's that should work with different types.
+data AltVar a b = AvLeft a | AvRight b
+  deriving (Show, Eq, Read, Ord)
+
+instance (FromHttpApiData a, FromHttpApiData b) => FromHttpApiData (AltVar a b) where
+  parseUrlPiece val =
+    case parseUrlPiece val of
+      Left err ->
+        case parseUrlPiece val of
+          Left err2 -> Left (err <> " " <> err2)
+          Right ok -> Right (AvRight ok)
+      Right ok -> Right (AvLeft ok)
+
 -- | A route parameter
 var :: (Typeable a, FromHttpApiData a) => Path (a ': '[]) 'Open
 var = VarCons Empty
@@ -39,10 +53,10 @@
 static :: String -> Path '[] 'Open
 static s =
   let pieces = filter (not . T.null) $ T.splitOn "/" $ T.pack s
-  in foldr StaticCons Empty pieces
+   in foldr StaticCons Empty pieces
 
 instance (a ~ '[], pathState ~ 'Open) => IsString (Path a pathState) where
-    fromString = static
+  fromString = static
 
 -- | The root of a path piece. Use to define a handler for "/"
 root :: Path '[] 'Open
@@ -69,9 +83,10 @@
 renderRoute' :: AllHave ToHttpApiData as => Path as 'Open -> HVect as -> [T.Text]
 renderRoute' Empty _ = []
 renderRoute' (StaticCons pathPiece pathXs) paramXs =
-    ( pathPiece : renderRoute' pathXs paramXs )
+  (pathPiece : renderRoute' pathXs paramXs)
 renderRoute' (VarCons pathXs) (val :&: paramXs) =
-    ( toUrlPiece val : renderRoute' pathXs paramXs)
+  (toUrlPiece val : renderRoute' pathXs paramXs)
+
 #if __GLASGOW_HASKELL__ < 800
 renderRoute' _ _ =
     error "This will never happen."
diff --git a/src/Web/Routing/Router.hs b/src/Web/Routing/Router.hs
--- a/src/Web/Routing/Router.hs
+++ b/src/Web/Routing/Router.hs
@@ -1,125 +1,134 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE KindSignatures #-}
-{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE RankNTypes #-}
-module Web.Routing.Router where
+{-# LANGUAGE TypeFamilies #-}
 
-import Web.Routing.SafeRouting
+module Web.Routing.Router where
 
 #if MIN_VERSION_base(4,8,0)
 #else
 import Control.Applicative
 #endif
 import Control.Monad.RWS.Strict
+import qualified Data.HashMap.Strict as HM
 import Data.Hashable
 import Data.Maybe
-import qualified Data.HashMap.Strict as HM
 import qualified Data.Text as T
+import Web.Routing.SafeRouting
 
-newtype RegistryT n b middleware reqTypes (m :: * -> *) a
-    = RegistryT
-    { runRegistryT :: RWST (PathInternal '[]) [middleware] (RegistryState n b reqTypes) m a
-    }
-    deriving (Monad, Functor, Applicative, MonadIO
-               , MonadReader (PathInternal '[])
-               , MonadWriter [middleware]
-               , MonadState (RegistryState n b reqTypes)
-               , MonadTrans
-               )
+newtype RegistryT n b middleware reqTypes (m :: * -> *) a = RegistryT
+  { runRegistryT :: RWST (PathInternal '[]) [middleware] (RegistryState n b reqTypes) m a
+  }
+  deriving
+    ( Monad,
+      Functor,
+      Applicative,
+      MonadIO,
+      MonadReader (PathInternal '[]),
+      MonadWriter [middleware],
+      MonadState (RegistryState n b reqTypes),
+      MonadTrans
+    )
 
-data RegistryState n b reqTypes
-   = RegistryState
-   { rs_registry :: !(HM.HashMap reqTypes (Registry n b))
-   , rs_anyMethod :: !(Registry n b)
-   }
+data RegistryState n b reqTypes = RegistryState
+  { rs_registry :: !(HM.HashMap reqTypes (Registry n b)),
+    rs_anyMethod :: !(Registry n b)
+  }
 
 hookAny ::
-    (Monad m, Eq reqTypes, Hashable reqTypes)
-    => reqTypes
-    -> ([T.Text] -> n b)
-    -> RegistryT n b middleware reqTypes m ()
+  (Monad m, Eq reqTypes, Hashable reqTypes) =>
+  reqTypes ->
+  ([T.Text] -> n b) ->
+  RegistryT n b middleware reqTypes m ()
 hookAny reqType action =
-    modify $ \rs ->
-        rs
-        { rs_registry =
-                let reg = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
-                in HM.insert reqType (fallbackRoute action reg) (rs_registry rs)
-        }
+  modify $ \rs ->
+    rs
+      { rs_registry =
+          let reg = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
+           in HM.insert reqType (fallbackRoute action reg) (rs_registry rs)
+      }
 
 hookAnyMethod ::
-    (Monad m)
-    => ([T.Text] -> n b)
-    -> RegistryT n b middleware reqTypes m ()
+  (Monad m) =>
+  ([T.Text] -> n b) ->
+  RegistryT n b middleware reqTypes m ()
 hookAnyMethod action =
-    modify $
+  modify $
     \rs ->
-        rs
+      rs
         { rs_anyMethod = fallbackRoute action (rs_anyMethod rs)
         }
 
 hookRoute ::
-    (Monad m, Eq reqTypes, Hashable reqTypes)
-    => reqTypes
-    -> PathInternal as
-    -> HVectElim' (n b) as
-    -> RegistryT n b middleware reqTypes m ()
+  (Monad m, Eq reqTypes, Hashable reqTypes) =>
+  reqTypes ->
+  PathInternal as ->
+  HVectElim' (n b) as ->
+  RegistryT n b middleware reqTypes m ()
 hookRoute reqType path action =
-    do basePath <- ask
-       modify $ \rs ->
-           rs { rs_registry =
-                    let reg = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
-                        reg' = defRoute (basePath </!> path) action reg
-                    in HM.insert reqType reg' (rs_registry rs)
-              }
+  do
+    basePath <- ask
+    modify $ \rs ->
+      rs
+        { rs_registry =
+            let reg = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
+                reg' = defRoute (basePath </!> path) action reg
+             in HM.insert reqType reg' (rs_registry rs)
+        }
 
 hookRouteAnyMethod ::
-    (Monad m)
-    => PathInternal as
-    -> HVectElim' (n b) as
-    -> RegistryT n b middleware reqTypes m ()
+  (Monad m) =>
+  PathInternal as ->
+  HVectElim' (n b) as ->
+  RegistryT n b middleware reqTypes m ()
 hookRouteAnyMethod path action =
-    do basePath <- ask
-       modify $ \rs ->
-           rs
-           { rs_anyMethod = defRoute (basePath </!> path) action (rs_anyMethod rs)
-           }
+  do
+    basePath <- ask
+    modify $ \rs ->
+      rs
+        { rs_anyMethod = defRoute (basePath </!> path) action (rs_anyMethod rs)
+        }
 
-middleware :: Monad m
-           => middleware
-           -> RegistryT n b middleware reqTypes m ()
+middleware ::
+  Monad m =>
+  middleware ->
+  RegistryT n b middleware reqTypes m ()
 middleware x = tell [x]
 
 swapMonad ::
-    Monad m
-    => (forall b. n b -> m b)
-    -> RegistryT x y middleware reqTypes n a
-    -> RegistryT x y middleware reqTypes m a
+  Monad m =>
+  (forall b. n b -> m b) ->
+  RegistryT x y middleware reqTypes n a ->
+  RegistryT x y middleware reqTypes m a
 swapMonad liftLower (RegistryT subReg) =
-    do parentSt <- get
-       basePath <- ask
-       (a, parentSt', middleware') <-
-           lift $ liftLower $ runRWST subReg basePath parentSt
-       put parentSt'
-       tell middleware'
-       return a
+  do
+    parentSt <- get
+    basePath <- ask
+    (a, parentSt', middleware') <-
+      lift $ liftLower $ runRWST subReg basePath parentSt
+    put parentSt'
+    tell middleware'
+    return a
 
-runRegistry :: (Monad m, Hashable reqTypes, Eq reqTypes)
-            => RegistryT n b middleware reqTypes m a
-            -> m (a, reqTypes -> [T.Text] -> [n b], [middleware])
+runRegistry ::
+  (Monad m, Hashable reqTypes, Eq reqTypes) =>
+  RegistryT n b middleware reqTypes m a ->
+  m (a, reqTypes -> [T.Text] -> [n b], [middleware])
 runRegistry (RegistryT rwst) =
-    do (val, st, w) <- runRWST rwst PI_Empty initSt
-       return (val, handleF (rs_anyMethod st) (rs_registry st), w)
-    where
-      handleF anyReg hm ty route =
-          let froute = filter (not . T.null) route
-          in case HM.lookup ty hm of
-               Nothing -> matchRoute anyReg froute
-               Just registry ->
-                   (matchRoute registry froute ++ matchRoute anyReg froute)
-      initSt =
-          RegistryState
-          { rs_registry = HM.empty
-          , rs_anyMethod = emptyRegistry
-          }
+  do
+    (val, st, w) <- runRWST rwst PI_Empty initSt
+    return (val, handleF (rs_anyMethod st) (rs_registry st), w)
+  where
+    handleF anyReg hm ty route =
+      let froute = filter (not . T.null) route
+       in case HM.lookup ty hm of
+            Nothing -> matchRoute anyReg froute
+            Just registry ->
+              (matchRoute registry froute ++ matchRoute anyReg froute)
+    initSt =
+      RegistryState
+        { rs_registry = HM.empty,
+          rs_anyMethod = emptyRegistry
+        }
diff --git a/src/Web/Routing/SafeRouting.hs b/src/Web/Routing/SafeRouting.hs
--- a/src/Web/Routing/SafeRouting.hs
+++ b/src/Web/Routing/SafeRouting.hs
@@ -1,18 +1,14 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
-module Web.Routing.SafeRouting where
 
-import Data.HVect hiding (null, length)
-import qualified Data.HVect as HV
-import qualified Data.PolyMap as PM
+module Web.Routing.SafeRouting where
 
-import Data.Maybe
 #if MIN_VERSION_base(4,11,0)
 #elif MIN_VERSION_base(4,9,0)
 import Data.Semigroup
@@ -23,15 +19,19 @@
 import Data.Monoid (Monoid (..), (<>))
 #endif
 import Control.DeepSeq (NFData (..))
-import Data.Typeable (Typeable)
-import Web.HttpApiData
+import Data.HVect hiding (length, null)
+import qualified Data.HVect as HV
 import qualified Data.HashMap.Strict as HM
+import Data.Maybe
+import qualified Data.PolyMap as PM
 import qualified Data.Text as T
+import Data.Typeable (Typeable)
+import Web.HttpApiData
 
 data RouteHandle m a
-   = forall as. RouteHandle (PathInternal as) (HVectElim as (m a))
+  = forall as. RouteHandle (PathInternal as) (HVectElim as (m a))
 
-newtype HVectElim' x ts = HVectElim' { flipHVectElim :: HVectElim ts x }
+newtype HVectElim' x ts = HVectElim' {flipHVectElim :: HVectElim ts x}
 
 type Registry m a = (PathMap (m a), [[T.Text] -> m a])
 
@@ -40,21 +40,21 @@
 
 defRoute :: PathInternal xs -> HVectElim' (m a) xs -> Registry m a -> Registry m a
 defRoute path action (m, call) =
-    ( insertPathMap (RouteHandle path (flipHVectElim action)) m
-    , call
-    )
+  ( insertPathMap (RouteHandle path (flipHVectElim action)) m,
+    call
+  )
 
 fallbackRoute :: ([T.Text] -> m a) -> Registry m a -> Registry m a
 fallbackRoute routeDef (m, call) = (m, call ++ [routeDef])
 
 matchRoute :: Registry m a -> [T.Text] -> [m a]
 matchRoute (m, cAll) pathPieces =
-    let matches = match m pathPieces
-        matches' =
-            if null matches
-            then matches ++ (map (\f -> f pathPieces) cAll)
-            else matches
-    in matches'
+  let matches = match m pathPieces
+      matches' =
+        if null matches
+          then matches ++ (map (\f -> f pathPieces) cAll)
+          else matches
+   in matches'
 
 data PathInternal (as :: [*]) where
   PI_Empty :: PathInternal '[] -- the empty path
@@ -62,22 +62,21 @@
   PI_VarCons :: (FromHttpApiData a, Typeable a) => PathInternal as -> PathInternal (a ': as) -- append a param to path
   PI_Wildcard :: PathInternal as -> PathInternal (T.Text ': as) -- append the rest of the route
 
-data PathMap x =
-  PathMap
-  { pm_subComponents :: [[T.Text] -> x]
-  , pm_here :: [x]
-  , pm_staticMap :: HM.HashMap T.Text (PathMap x)
-  , pm_polyMap :: PM.PolyMap FromHttpApiData PathMap x
-  , pm_wildcards :: [T.Text -> x]
+data PathMap x = PathMap
+  { pm_subComponents :: [[T.Text] -> x],
+    pm_here :: [x],
+    pm_staticMap :: HM.HashMap T.Text (PathMap x),
+    pm_polyMap :: PM.PolyMap FromHttpApiData PathMap x,
+    pm_wildcards :: [T.Text -> x]
   }
 
 instance Functor PathMap where
   fmap f (PathMap c h s p w) =
-      PathMap (fmap f <$> c) (f <$> h) (fmap f <$> s) (f <$> p) (fmap f <$> w)
+    PathMap (fmap f <$> c) (f <$> h) (fmap f <$> s) (f <$> p) (fmap f <$> w)
 
 instance NFData x => NFData (PathMap x) where
   rnf (PathMap c h s p w) =
-      rnf c `seq` rnf h `seq` rnf s `seq` PM.rnfHelper rnf p `seq` rnf w
+    rnf c `seq` rnf h `seq` rnf s `seq` PM.rnfHelper rnf p `seq` rnf w
 
 emptyPathMap :: PathMap x
 emptyPathMap = PathMap mempty mempty mempty PM.empty mempty
@@ -90,31 +89,32 @@
   mempty = emptyPathMap
   mappend = (<>)
 
-updatePathMap
-  :: (forall y. (ctx -> y) -> PathMap y -> PathMap y)
-  -> PathInternal ts
-  -> (HVect ts -> ctx -> x)
-  -> PathMap x
-  -> PathMap x
+updatePathMap ::
+  (forall y. (ctx -> y) -> PathMap y -> PathMap y) ->
+  PathInternal ts ->
+  (HVect ts -> ctx -> x) ->
+  PathMap x ->
+  PathMap x
 updatePathMap updateFn path action pm@(PathMap c h s p w) =
   case path of
     PI_Empty -> updateFn (action HNil) pm
     PI_StaticCons pathPiece path' ->
       let subPathMap = fromMaybe emptyPathMap (HM.lookup pathPiece s)
-      in PathMap c h (HM.insert pathPiece (updatePathMap updateFn path' action subPathMap) s) p w
+       in PathMap c h (HM.insert pathPiece (updatePathMap updateFn path' action subPathMap) s) p w
     PI_VarCons path' ->
-      let alterFn = Just . updatePathMap updateFn path' (\vs ctx v -> action (v :&: vs) ctx)
-                         . fromMaybe emptyPathMap
-      in PathMap c h s (PM.alter alterFn p) w
+      let alterFn =
+            Just . updatePathMap updateFn path' (\vs ctx v -> action (v :&: vs) ctx)
+              . fromMaybe emptyPathMap
+       in PathMap c h s (PM.alter alterFn p) w
     PI_Wildcard PI_Empty ->
       let (PathMap _ (action' : _) _ _ _) = updateFn (\ctx rest -> action (rest :&: HNil) ctx) emptyPathMap
-      in PathMap c h s p $ action' : w
+       in PathMap c h s p $ action' : w
     PI_Wildcard _ -> error "Shouldn't happen"
 
 insertPathMap' :: PathInternal ts -> (HVect ts -> x) -> PathMap x -> PathMap x
 insertPathMap' path action =
   let updateHeres y (PathMap c h s p w) = PathMap c (y () : h) s p w
-  in updatePathMap updateHeres path (const <$> action)
+   in updatePathMap updateHeres path (const <$> action)
 
 singleton :: PathInternal ts -> HVectElim ts x -> PathMap x
 singleton path action = insertPathMap' path (HV.uncurry action) mempty
@@ -125,7 +125,7 @@
 insertSubComponent' :: PathInternal ts -> (HVect ts -> [T.Text] -> x) -> PathMap x -> PathMap x
 insertSubComponent' path subComponent =
   let updateSubComponents y (PathMap c h s p w) = PathMap (y : c) h s p w
-  in updatePathMap updateSubComponents path subComponent
+   in updatePathMap updateSubComponents path subComponent
 
 insertSubComponent :: Functor m => RouteHandle m ([T.Text] -> a) -> PathMap (m a) -> PathMap (m a)
 insertSubComponent (RouteHandle path comp) =
@@ -133,17 +133,19 @@
 
 match :: PathMap x -> [T.Text] -> [x]
 match (PathMap c h s p w) pieces =
-  map ($ pieces) c ++
-  case pieces of
-    [] -> h ++ fmap ($ "") w
-    (pp:pps) ->
-      let staticMatches = maybeToList (HM.lookup pp s) >>= flip match pps
-          varMatches = PM.lookupConcat (either (const Nothing) Just $ parseUrlPiece pp)
-                         (\piece pathMap' -> fmap ($ piece) (match pathMap' pps)) p
-          routeRest = combineRoutePieces pieces
-          wildcardMatches = fmap ($ routeRest) w
-      in staticMatches ++ varMatches ++ wildcardMatches
-
+  map ($ pieces) c
+    ++ case pieces of
+      [] -> h ++ fmap ($ "") w
+      (pp : pps) ->
+        let staticMatches = maybeToList (HM.lookup pp s) >>= flip match pps
+            varMatches =
+              PM.lookupConcat
+                (either (const Nothing) Just $ parseUrlPiece pp)
+                (\piece pathMap' -> fmap ($ piece) (match pathMap' pps))
+                p
+            routeRest = combineRoutePieces pieces
+            wildcardMatches = fmap ($ routeRest) w
+         in staticMatches ++ varMatches ++ wildcardMatches
 
 (</!>) :: PathInternal as -> PathInternal bs -> PathInternal (Append as bs)
 (</!>) PI_Empty xs = xs
@@ -158,19 +160,19 @@
 parse PI_Empty [] = Just HNil
 parse _ [] = Nothing
 parse path pathComps@(pathComp : xs) =
-    case path of
-      PI_Empty -> Nothing
-      PI_StaticCons pathPiece pathXs ->
-          if pathPiece == pathComp
-          then parse pathXs xs
-          else Nothing
-      PI_VarCons pathXs ->
-          case parseUrlPiece pathComp of
-            Left _ -> Nothing
-            Right val ->
-                let finish = parse pathXs xs
-                in fmap (\parsedXs -> val :&: parsedXs) finish
-      PI_Wildcard PI_Empty ->
-        Just $ combineRoutePieces pathComps :&: HNil
-      PI_Wildcard _ ->
-        error "Shouldn't happen"
+  case path of
+    PI_Empty -> Nothing
+    PI_StaticCons pathPiece pathXs ->
+      if pathPiece == pathComp
+        then parse pathXs xs
+        else Nothing
+    PI_VarCons pathXs ->
+      case parseUrlPiece pathComp of
+        Left _ -> Nothing
+        Right val ->
+          let finish = parse pathXs xs
+           in fmap (\parsedXs -> val :&: parsedXs) finish
+    PI_Wildcard PI_Empty ->
+      Just $ combineRoutePieces pathComps :&: HNil
+    PI_Wildcard _ ->
+      error "Shouldn't happen"
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,9 +1,9 @@
 module Main where
 
-import qualified Web.Routing.SafeRoutingSpec
-
 import Test.Hspec
+import qualified Web.Routing.SafeRoutingSpec
 
 main :: IO ()
-main = hspec $
+main =
+  hspec $
     Web.Routing.SafeRoutingSpec.spec
diff --git a/test/Web/Routing/SafeRoutingSpec.hs b/test/Web/Routing/SafeRoutingSpec.hs
--- a/test/Web/Routing/SafeRoutingSpec.hs
+++ b/test/Web/Routing/SafeRoutingSpec.hs
@@ -1,28 +1,28 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE OverloadedStrings #-}
-module Web.Routing.SafeRoutingSpec where
+{-# LANGUAGE TypeFamilies #-}
 
-import Web.Routing.Combinators
-import Web.Routing.Router
-import Web.Routing.SafeRouting
+module Web.Routing.SafeRoutingSpec where
 
 import Control.Monad.Identity
 import Control.Monad.RWS.Strict
 import Data.HVect hiding (singleton)
+import qualified Data.HashMap.Strict as HM
 import Data.List
 import Data.Maybe
-import Test.Hspec
-import qualified Data.HashMap.Strict as HM
 import qualified Data.Text as T
+import Test.Hspec
+import Web.Routing.Combinators
+import Web.Routing.Router
+import Web.Routing.SafeRouting
 
 data ReturnVar
-   = IntVar Int
-   | StrVar T.Text
-   | BoolVar Bool
-   | ListVar [ReturnVar]
-   deriving (Show, Eq, Read, Ord)
+  = IntVar Int
+  | StrVar T.Text
+  | ListVar [ReturnVar]
+  | EitherVar (AltVar Int T.Text)
+  deriving (Show, Eq, Read, Ord)
 
 defR :: (Monad m, m ReturnVar ~ x) => Path ts ps -> HVectElim ts x -> RegistryT m ReturnVar middleware Bool m ()
 defR path action = hookRoute True (toInternalPath path) (HVectElim' action)
@@ -32,93 +32,108 @@
 
 -- TODO: abstract this code, move into AbstractRouter
 defSubComponent ::
-    ( Monad m
+  ( Monad m,
 #if __GLASGOW_HASKELL__ <= 708
     , Functor m
 #endif
-    , m ([T.Text] -> ReturnVar) ~ x
-    )
-    => Path ts ps
-    -> HVectElim ts x
-    -> RegistryT m ReturnVar middleware Bool m ()
+    m ([T.Text] -> ReturnVar) ~ x
+  ) =>
+  Path ts ps ->
+  HVectElim ts x ->
+  RegistryT m ReturnVar middleware Bool m ()
 defSubComponent path comp =
-    do let reqType = True
-       basePath <- ask
-       modify $ \rs ->
-           rs { rs_registry =
-                    let (reg, fb) = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
-                        reg' = insertSubComponent (RouteHandle (basePath </!> toInternalPath path) comp) reg
-                    in HM.insert reqType (reg', fb) (rs_registry rs)
-              }
+  do
+    let reqType = True
+    basePath <- ask
+    modify $ \rs ->
+      rs
+        { rs_registry =
+            let (reg, fb) = fromMaybe emptyRegistry (HM.lookup reqType (rs_registry rs))
+                reg' = insertSubComponent (RouteHandle (basePath </!> toInternalPath path) comp) reg
+             in HM.insert reqType (reg', fb) (rs_registry rs)
+        }
 
 spec :: Spec
 spec =
-    describe "SafeRouting Spec" $
-    do it "should match known routes" $
-          do checkRoute "" [StrVar "root"]
-             checkRoute "/" [StrVar "root"]
-             checkRoute "/bar" [StrVar "bar"]
-       it "should match any routes" $
-          do checkRoute' "/any" True [StrVar "any", StrVar "any"] -- two due to hookAny
-             checkRoute' "/any" False [StrVar "any"]
-       it "should capture variables in routes" $
-          do checkRoute "/bar/23/baz" [IntVar 23]
-             checkRoute "/bar/23/baz/" [IntVar 23]
-             checkRoute "/bar/23/baz/100" [ListVar [IntVar 23, IntVar 100]]
-             checkRoute "/bar/23/100" [ListVar [IntVar 23, IntVar 100]]
-             checkRoute "/entry/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
-             checkRoute "/entry/bytags/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
-             checkRoute "/entry/2/rel/3"  [ListVar [IntVar 2, IntVar 3]]
-       it "should handle multiple possible matches correctly" $
-          do checkRoute "/bar/5" [IntVar 5, StrVar "5"]
-             checkRoute "/bar/bingo" [StrVar "bar/bingo", StrVar "bingo"]
-             checkRoute "/entry/1/audit" [IntVar 1,ListVar [IntVar 1,StrVar "audit"]]
-       it "should have a catch all route" $
-          do checkRoute "/aslkdjk/asdaskl/aslkjd" [StrVar "aslkdjk/asdaskl/aslkjd"]
-             checkRoute "/zuiasf/zuiasf" [StrVar "zuiasf/zuiasf"]
-       it "should hand over remaining path pieces to subcomponents" $
-          do checkRoute "/subcomponent/blog/foo/bar/nanana" [StrVar "blog:foo?bar?nanana"]
-       it "should handle wildcard routes" $
-          do checkRoute "/wildcard/" [StrVar ""]
-             checkRoute "/wildcard/some/additional/data" [StrVar "some/additional/data"]
-    where
-      pieces :: T.Text -> [T.Text]
-      pieces = filter (not . T.null) . T.splitOn "/"
+  describe "SafeRouting Spec" $
+    do
+      it "should match known routes" $
+        do
+          checkRoute "" [StrVar "root"]
+          checkRoute "/" [StrVar "root"]
+          checkRoute "/bar" [StrVar "bar"]
+      it "should match any routes" $
+        do
+          checkRoute' "/any" True [StrVar "any", StrVar "any"] -- two due to hookAny
+          checkRoute' "/any" False [StrVar "any"]
+      it "should capture variables in routes" $
+        do
+          checkRoute "/bar/23/baz" [IntVar 23]
+          checkRoute "/bar/23/baz/" [IntVar 23]
+          checkRoute "/bar/23/baz/100" [ListVar [IntVar 23, IntVar 100]]
+          checkRoute "/bar/23/100" [ListVar [IntVar 23, IntVar 100]]
+          checkRoute "/entry/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
+          checkRoute "/entry/bytags/344/2014-20-14T12:23" [ListVar [IntVar 344, StrVar "2014-20-14T12:23"]]
+          checkRoute "/entry/2/rel/3" [ListVar [IntVar 2, IntVar 3]]
+      it "should handle multiple possible matches correctly" $
+        do
+          checkRoute "/bar/5" [IntVar 5, StrVar "5"]
+          checkRoute "/bar/bingo" [StrVar "bar/bingo", StrVar "bingo"]
+          checkRoute "/entry/1/audit" [IntVar 1, ListVar [IntVar 1, StrVar "audit"]]
+      it "should have a catch all route" $
+        do
+          checkRoute "/aslkdjk/asdaskl/aslkjd" [StrVar "aslkdjk/asdaskl/aslkjd"]
+          checkRoute "/zuiasf/zuiasf" [StrVar "zuiasf/zuiasf"]
+      it "should hand over remaining path pieces to subcomponents" $
+        do checkRoute "/subcomponent/blog/foo/bar/nanana" [StrVar "blog:foo?bar?nanana"]
+      it "should handle wildcard routes" $
+        do
+          checkRoute "/wildcard/" [StrVar ""]
+          checkRoute "/wildcard/some/additional/data" [StrVar "some/additional/data"]
+      it "correctly handles alternatives" $
+        do
+          checkRoute "/either/1" [EitherVar (AvLeft 1)]
+          checkRoute "/either/aaa" [EitherVar (AvRight "aaa")]
+  where
+    pieces :: T.Text -> [T.Text]
+    pieces = filter (not . T.null) . T.splitOn "/"
 
-      checkRoute' :: T.Text -> Bool -> [ReturnVar] -> Expectation
-      checkRoute' r b x =
-          let matches = handleFun b (pieces r)
-          in sort (map runIdentity matches) `shouldBe` sort x
+    checkRoute' :: T.Text -> Bool -> [ReturnVar] -> Expectation
+    checkRoute' r b x =
+      let matches = handleFun b (pieces r)
+       in sort (map runIdentity matches) `shouldBe` sort x
 
-      checkRoute :: T.Text -> [ReturnVar] -> Expectation
-      checkRoute = flip checkRoute' True
+    checkRoute :: T.Text -> [ReturnVar] -> Expectation
+    checkRoute = flip checkRoute' True
 
-      handleFun :: Bool -> [T.Text] -> [Identity ReturnVar]
-      handleFun = handleFun'
+    handleFun :: Bool -> [T.Text] -> [Identity ReturnVar]
+    handleFun = handleFun'
 
-      (_, handleFun', _) = runIdentity (runRegistry handleDefs)
+    (_, handleFun', _) = runIdentity (runRegistry handleDefs)
 
-      handleDefs =
-          do defR root $ return (StrVar "root")
-             defRAny "any" $ pure (StrVar "any")
-             defR "bar" $ return (StrVar "bar")
-             defR ("bar" </> var) (return . IntVar)
-             defR ("bar" </> var </> "baz") (return . IntVar)
-             defR ("bar" </> var </> "baz" </> var) $ \i i2 ->
-                 return (ListVar [IntVar i, IntVar i2])
-             defR ("bar" </> var </> var) $ \i i2 ->
-                 return (ListVar [IntVar i, IntVar i2])
-             defR ("entry" </> var </> var) $ \i st ->
-                 return (ListVar [IntVar i, StrVar st])
-             defR ("entry/bytags" </> var </> var) $ \i st ->
-                 return (ListVar [IntVar i, StrVar st])
-             defR ("entry" </> var </> "rel" </> var) $ \i i2 ->
-                 return (ListVar [IntVar i, IntVar i2])
-             defR ("bar" </> "bingo") $ return (StrVar "bar/bingo")
-             defR ("bar" </> var) $ (return . StrVar . T.pack)
-             defR ("entry" </> var </> "audit") (return . IntVar)
-             defSubComponent ("subcomponent" </> var) $ \name ->
-               return $ \ps -> StrVar $ name <> ":" <> T.intercalate "?" ps
-             defR ("wildcard" </> wildcard) $ \rest ->
-               return $ StrVar rest
-             hookAny True (return . StrVar . T.intercalate "/")
+    handleDefs =
+      do
+        defR root $ return (StrVar "root")
+        defRAny "any" $ pure (StrVar "any")
+        defR "bar" $ return (StrVar "bar")
+        defR ("bar" </> var) (return . IntVar)
+        defR ("bar" </> var </> "baz") (return . IntVar)
+        defR ("bar" </> var </> "baz" </> var) $ \i i2 ->
+          return (ListVar [IntVar i, IntVar i2])
+        defR ("bar" </> var </> var) $ \i i2 ->
+          return (ListVar [IntVar i, IntVar i2])
+        defR ("entry" </> var </> var) $ \i st ->
+          return (ListVar [IntVar i, StrVar st])
+        defR ("entry/bytags" </> var </> var) $ \i st ->
+          return (ListVar [IntVar i, StrVar st])
+        defR ("entry" </> var </> "rel" </> var) $ \i i2 ->
+          return (ListVar [IntVar i, IntVar i2])
+        defR ("bar" </> "bingo") $ return (StrVar "bar/bingo")
+        defR ("bar" </> var) (return . StrVar)
+        defR ("either" </> var) (return . EitherVar)
+        defR ("entry" </> var </> "audit") (return . IntVar)
+        defSubComponent ("subcomponent" </> var) $ \name ->
+          return $ \ps -> StrVar $ name <> ":" <> T.intercalate "?" ps
+        defR ("wildcard" </> wildcard) $ \rest ->
+          return $ StrVar rest
+        hookAny True (return . StrVar . T.intercalate "/")
