packages feed

urlpath 0.0.6 → 0.1

raw patch · 4 files changed

+235/−207 lines, 4 filesdep +QuickCheckdep +hspecdep +quickcheck-instancesdep ~base

Dependencies added: QuickCheck, hspec, quickcheck-instances

Dependency ranges changed: base

Files

src/UrlPath.hs view
@@ -5,7 +5,7 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE TypeFamilies #-}-+{-# LANGUAGE RankNTypes #-}  module UrlPath     ( UrlReader (..)@@ -14,107 +14,90 @@  import UrlPath.Types -import qualified Data.Text as T--import Control.Monad.Trans-import Control.Monad.Reader.Class-+import Data.String import Data.Monoid import Control.Applicative import Control.Monad import Data.Functor.Identity+import Control.Monad.Trans+import Control.Monad.Reader.Class  --- | Convenience typeclass for a uniform interface into pure @Reader@-like --- monads.-class MonadReader T.Text m => UrlReader (m :: * -> *) where-  runUrlReader :: m a -- ^ Monadic reader-like computation-               -> T.Text -- ^ @T.Text@ index-               -> a -- ^ Result -instance MonadReader T.Text Identity where-  ask = return ""--instance UrlReader Identity where-  runUrlReader x _ = runIdentity x--instance UrlReader AbsoluteUrl where-  runUrlReader = runAbsoluteUrl--instance UrlReader RelativeUrl where-  runUrlReader = runRelativeUrl--instance UrlReader GroundedUrl where-  runUrlReader = runGroundedUrl---- | @Url@ takes an input type @a@, and returns a modality @f@ around @T.Text@.-class MonadReader T.Text m => Url a m where-  renderUrl :: a -- ^ Url-like type (@UrlString@ or @T.Text@).-            -> m T.Text -- ^ Rendered Url in some context @f@--instance Url T.Text Identity where-  renderUrl = Identity+-- | @Url@ is a relationship between an underlying (monomorphic) string type+-- @string@, and a deployment context @m@. We try to make the deployment and +-- implementation type coercible at the top level - coercing your final +-- expression to @String@ or @T.Text@ will have /all/ use-cases coerce to that +-- type, similarly with the deployment scheme.+--+-- We chose to not force @MonadReader@ as a superclass for @m@ due to the +-- monomorphic demand on functional dependencies.+class ( IsString string, Monoid string, MonadReader string m ) =>+          Url string (m :: * -> *) where+  url :: UrlString string -- ^ Url type+      -> m string -- ^ Rendered Url in some context.+  stringUrl :: string -- ^ raw string+            -> m string -- ^ Rendered string in some context. -instance Url UrlString Identity where-  renderUrl = Identity . expandRelative+-- | Overload deployment schemes with this - then, all that's needed is a type +-- coercion to change deployment. This only works with flat (co)monads, so monad +-- transformers are out.+class Url string m => UrlReader string m where+  runUrlReader :: Url string m =>+                  m string -- ^ Monad with result @string@+               -> string -- ^ Reader index+               -> string -- ^ Final result -instance Url UrlString RelativeUrl where-  renderUrl x = RelativeUrl $ \_ -> expandRelative x+-- * Monads -instance Url UrlString GroundedUrl where-  renderUrl x = GroundedUrl $ \_ -> expandGrounded x+instance ( Monoid a+         , IsString a ) => Url a (RelativeUrl a) where+  url = RelativeUrl . const . expandRelative+  stringUrl x = RelativeUrl $ const $ expandRelative $ UrlString x [] -instance Url UrlString AbsoluteUrl where-  renderUrl = expandAbsolute+instance ( Monoid a+         , IsString a ) => UrlReader a (RelativeUrl a) where+  runUrlReader = runRelativeUrl -instance Monad m => Url UrlString (RelativeUrlT m) where-  renderUrl x = RelativeUrlT $ \_ -> return $ expandRelative x+--- -instance Monad m => Url UrlString (GroundedUrlT m) where-  renderUrl x = GroundedUrlT $ \_ -> return $ expandGrounded x+instance ( Monoid a+         , IsString a ) => Url a (GroundedUrl a) where+  url = GroundedUrl . const . expandGrounded+  stringUrl x = GroundedUrl $ const $ expandGrounded $ UrlString x [] -instance Monad m => Url UrlString (AbsoluteUrlT m) where-  renderUrl = expandAbsolute+instance ( Monoid a+         , IsString a ) => UrlReader a (GroundedUrl a) where+  runUrlReader = runGroundedUrl -instance Url T.Text RelativeUrl where-  renderUrl x = RelativeUrl $ \_ -> expandRelative $ UrlString x []+--- -instance Url T.Text GroundedUrl where-  renderUrl x = GroundedUrl $ \_ -> expandGrounded $ UrlString x []+instance ( Monoid a+         , IsString a ) => Url a (AbsoluteUrl a) where+  url = expandAbsolute+  stringUrl x = expandAbsolute $ UrlString x [] -instance Url T.Text AbsoluteUrl where-  renderUrl x = expandAbsolute $ UrlString x []+-- | Hand-off host prepending to the MonadReader instance+instance ( Monoid a+         , IsString a ) => UrlReader a (AbsoluteUrl a) where+  runUrlReader = runAbsoluteUrl -instance Monad m => Url T.Text (RelativeUrlT m) where-  renderUrl x = RelativeUrlT $ \_ -> return $ expandRelative $ UrlString x []+-- * Transformers -instance Monad m => Url T.Text (GroundedUrlT m) where-  renderUrl x = GroundedUrlT $ \_ -> return $ expandGrounded $ UrlString x []+instance ( Monad m+         , Monoid a+         , IsString a ) => Url a (RelativeUrlT a m) where+  url = RelativeUrlT . const . return . expandRelative+  stringUrl x = RelativeUrlT $ const $ return $ expandRelative $ UrlString x [] -instance Monad m => Url T.Text (AbsoluteUrlT m) where-  renderUrl x = expandAbsolute $ UrlString x []+instance ( Monad m+         , Monoid a+         , IsString a ) => Url a (GroundedUrlT a m) where+  url = GroundedUrlT . const . return . expandGrounded+  stringUrl x = GroundedUrlT $ const $ return $ expandGrounded $ UrlString x [] --- * Example------ | Here is an example:------ > {-# LANGUAGE OverloadedStrings #-}--- > {-# LANGUAGE ExtendedDefaultRules #-}--- >--- > import Lucid--- > import Lucid.Path--- > import qualified Data.Text as T--- > import qualified Data.Text.Lazy as LT--- >--- > foo :: LT.Text--- > foo = (runUrlReader $ renderTextT bar) "example.com"--- >--- > bar :: HtmlT AbsoluteUrl ()--- > bar = do--- >   url <- lift $ renderUrl $ "foo.php" <?> ("bar","baz")--- >   script_ [src_ url] ""------ Where @foo@ is now------ > Lucid> foo--- > "<script src=\"example.com/foo.php?bar=baz\"></script>"+instance ( Monad m+         , Monoid a+         , IsString a ) => Url a (AbsoluteUrlT a m) where+  url = expandAbsolute+  stringUrl x = expandAbsolute $ UrlString x []
src/UrlPath/Types.hs view
@@ -1,88 +1,94 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DeriveFunctor #-}  module UrlPath.Types where -import qualified Data.Text as T-+import Data.String import Data.Monoid import Control.Applicative import Control.Monad import Control.Monad.Trans import Control.Monad.Reader.Class --- | A GET parameter encoded in a Url.-data GETParam = GETParam { key :: !T.Text -- ^ Key for a get parameter.-                         , val :: !T.Text -- ^ Value for the key.-                         }-  deriving (Show, Eq)---- | Render a GET parameter pair.-renderGETParam :: GETParam-               -> T.Text-renderGETParam (GETParam k v) =-  "&" <> k <> "=" <> v---- | A Url string - a target page and GET parameters.-data UrlString = UrlString { target :: !T.Text -- ^ Relative base file - eg) @"foo.php"@ in @"foo.php?bar=baz"@.-                           , params :: [GETParam] -- ^ GET Parameters.-                           }-  deriving (Show, Eq)+-- | A Url string - a target page and GET parameters. We only require a +-- constraint of @IsString@ so that construction can be convenient, but +-- rendering will require a monomorphic type.+data UrlString a where+  UrlString :: ( IsString a+               , Monoid a ) =>+               a+            -> [(a, a)]+            -> UrlString a --- | Render a Url String /simply/ - this is equivalent to @expandRelative@.-renderUrlString :: UrlString-                -> T.Text-renderUrlString (UrlString t []) = t-renderUrlString (UrlString t [GETParam k v]) =-  t <> "?" <> k <> "=" <> v-renderUrlString (UrlString t (GETParam k v : ps)) =+-- | We choose to not provide a @Show@ instance for @UrlString@ to evade the +-- @String@ demand.+showUrlString :: UrlString a+              -> a+showUrlString (UrlString !t []) = t+showUrlString (UrlString !t ((!k,!v):xs)) =   t <> "?" <> k <> "=" <> v <>-    foldr (\x acc -> acc <> renderGETParam x) "" ps+    foldl (\acc (x,y) -> acc <> "&" <> x <> "=" <> y) "" xs  --- | Lifts a target path with some GET parameter chunks into a @UrlString@.-(<?>) :: T.Text -- ^ Target string-      -> (T.Text, T.Text) -- ^ GET Parameter-      -> UrlString-t <?> (k,v) = UrlString t [GETParam k v]+-- | Lifts a raw target path and a GET parameter pair into a @UrlString@.+(<?>) :: ( IsString a+         , Monoid a ) =>+         a -- ^ Target string+      -> (a, a) -- ^ GET Parameter+      -> UrlString a+(<?>) !t !kv = UrlString t [kv]  infixl 9 <?> --- | Adds more GET parameters to a @UrlString@.-(<&>) :: UrlString -- ^ Old Url-      -> (T.Text, T.Text) -- ^ Additional GET Parameter-      -> UrlString-old <&> (k,v) = UrlString (target old) $ params old ++ [GETParam k v]+-- | Adds another GET parameter pair to a @UrlString@.+(<&>) :: ( IsString a+         , Monoid a ) =>+         UrlString a -- ^ Old Url+      -> (a, a) -- ^ Additional GET Parameter+      -> UrlString a+(<&>) (UrlString !t !p) !kv = UrlString t $ p ++ [kv]  infixl 8 <&>   -- | Render the Url String as relative-expandRelative :: UrlString-               -> T.Text-expandRelative = renderUrlString+expandRelative :: ( IsString a+                  , Monoid a ) =>+                  UrlString a+               -> a+expandRelative = showUrlString  -- | Render the Url String as grounded-expandGrounded :: UrlString-               -> T.Text-expandGrounded x = "/" <> renderUrlString x+expandGrounded :: ( IsString a+                  , Monoid a ) =>+                  UrlString a+               -> a+expandGrounded !x = "/" <> showUrlString x --- | Render the Url String as absolute - getting the root from a MonadReader.-expandAbsolute :: (MonadReader T.Text m) =>-                  UrlString-               -> m T.Text-expandAbsolute x = do-  root <- ask-  return $ root <> "/" <> renderUrlString x+-- | Render the Url String as absolute - getting the root from a @MonadReader@ +-- context. The @Monoid@ instance will be decided monomorphically, therefore a +-- type signature will be needed when ran.+expandAbsolute :: ( MonadReader a m+                  , IsString a+                  , Monoid a ) =>+                  UrlString a+               -> m a+expandAbsolute !x = do+  host <- ask+  return $ host <> "/" <> showUrlString x  -- | Render the Url String as absolute, but with your own configuration type.---             +-- -- > data SiteConfig = SiteConfig { host :: T.Text -- >                              , cdnHost :: T.Text -- >                              } -- >   deriving (Show, Eq)--- >           +-- > -- > foo :: HtmlT (Reader SiteConfig) () -- > foo = do -- >   url <- lift $ expandAbsoluteWith ("foo.php" <?> ("bar","baz")) host@@ -91,155 +97,161 @@ -- > bar :: LT.Text  -- > bar = (runReader (runTextT foo)) $ -- >   SiteConfig "example.com" "cdn.example.com"-expandAbsoluteWith :: (MonadReader a m) =>-                      UrlString-                   -> (a -> T.Text)-                   -> m T.Text-expandAbsoluteWith x f = do+expandAbsoluteWith :: ( MonadReader a m+                      , IsString a+                      , Monoid a ) =>+                      UrlString a+                   -> (a -> a)+                   -> m a+expandAbsoluteWith !x f = do   root <- liftM f ask-  return $ root <> "/" <> renderUrlString x+  return $ root <> "/" <> showUrlString x  -- | Rendering mode transformer. This isn't an instance of @UrlReader@ - to use,  -- simple @lift@ as many levels as you need: --  -- > foo :: Monad m => HtmlT (RelativeUrlT m) () -- > foo = do--- >   url <- lift $ renderUrl $ "foo.php" <?> ("bar","baz")--- >   script_ [src_ url] ""+-- >   path <- lift $ url $ "foo.php" <?> ("bar","baz")+-- >   script_ [src_ path] "" -- -- When rendering @foo@, simply use the Transformer's @run@ function to convert  -- it to a reader: ----- > bar :: Monad m => m LT.Text+-- > bar :: ( Monad m, IsString a, Monoid a ) => m a -- > bar = (runRelativeUrlT (renderTextT foo)) "example.com"------ It is generally simpler (but more restrictive) to use the non-transformer --- variety.-newtype RelativeUrlT m a = RelativeUrlT { runRelativeUrlT :: T.Text -> m a }--instance Functor f => Functor (RelativeUrlT f) where-  fmap f x = RelativeUrlT $ \a ->-    fmap f (runRelativeUrlT x a)+newtype RelativeUrlT h m a = RelativeUrlT { runRelativeUrlT :: h -> m a }+  deriving Functor -instance Applicative f => Applicative (RelativeUrlT f) where+instance Applicative f => Applicative (RelativeUrlT h f) where   (<*>) f x = RelativeUrlT $ \a ->     (<*>) (runRelativeUrlT f a) (runRelativeUrlT x a) -instance Monad m => Monad (RelativeUrlT m) where+instance Monad m => Monad (RelativeUrlT h m) where   return x = RelativeUrlT $ \_ -> return x   m >>= f = RelativeUrlT $ \a ->     runRelativeUrlT m a >>= (\x -> runRelativeUrlT (f x) a) -instance MonadTrans RelativeUrlT where+instance MonadTrans (RelativeUrlT h) where   lift m = RelativeUrlT (const m) -instance Monad m => MonadReader T.Text (RelativeUrlT m) where+instance ( Monad m+         , IsString a ) => MonadReader a (RelativeUrlT a m) where   ask = return "" +instance MonadIO m => MonadIO (RelativeUrlT a m) where+  liftIO = lift . liftIO+ -- | Concrete Monad for automatically coercing HtmlT's to use a mode of Url  -- rendering (relative, grounded, or absolute). -- -- > foo :: HtmlT RelativeUrl () -- > foo = do--- >   url <- lift $ renderUrl $ "foo.php" <?> ("bar","baz")--- >   script_ [src_ url] ""+-- >   path <- lift $ url $ "foo.php" <?> ("bar","baz")+-- >   script_ [src_ path] "" ----- when rendering these simple monads for automatic conversion via coercion, use --- the @runUrlReader@ member function of the @UrlReader@ typeclass:+-- when running the monad reader, use the @runUrlReader@ member function of the+-- @UrlReader@ typeclass: ----- > bar :: LT.Text+-- > bar :: ( IsString a, Monoid a ) => a -- > bar = (runUrlReader (renderTextT foo)) "example.com" ----- To change the mode of rendering, simple change the coerced type of @foo@.-newtype RelativeUrl a = RelativeUrl { runRelativeUrl :: T.Text -> a }--instance Functor RelativeUrl where-  fmap f x = RelativeUrl $ \a -> f $ runRelativeUrl x a+-- To change the deployment sheme, simply coerce the environment monad in @foo@.+newtype RelativeUrl h a = RelativeUrl { runRelativeUrl :: h -> a }+  deriving Functor -instance Applicative RelativeUrl where+instance Applicative (RelativeUrl h) where   (<*>) f x = RelativeUrl $ \a ->     runRelativeUrl f a (runRelativeUrl x a) -instance Monad RelativeUrl where+instance Monad (RelativeUrl h) where   return x = RelativeUrl $ const x   m >>= f = RelativeUrl $ \a ->     (\y -> runRelativeUrl (f y) a) (runRelativeUrl m a) -instance MonadReader T.Text RelativeUrl where+instance IsString a => MonadReader a (RelativeUrl a) where   ask = return "" -newtype GroundedUrlT m a = GroundedUrlT { runGroundedUrlT :: T.Text -> m a }+newtype GroundedUrlT h m a = GroundedUrlT { runGroundedUrlT :: h -> m a } -instance Functor f => Functor (GroundedUrlT f) where+instance Functor f => Functor (GroundedUrlT h f) where   fmap f x = GroundedUrlT $ \a ->     fmap f (runGroundedUrlT x a) -instance Applicative f => Applicative (GroundedUrlT f) where+instance Applicative f => Applicative (GroundedUrlT h f) where   (<*>) f x = GroundedUrlT $ \a ->     (<*>) (runGroundedUrlT f a) (runGroundedUrlT x a) -instance Monad m => Monad (GroundedUrlT m) where+instance Monad m => Monad (GroundedUrlT h m) where   return x = GroundedUrlT $ \_ -> return x   m >>= f = GroundedUrlT $ \a ->     runGroundedUrlT m a >>= (\x -> runGroundedUrlT (f x) a) -instance MonadTrans GroundedUrlT where+instance MonadTrans (GroundedUrlT h) where   lift m = GroundedUrlT (const m) -instance Monad m => MonadReader T.Text (GroundedUrlT m) where+instance ( Monad m+         , IsString a ) => MonadReader a (GroundedUrlT a m) where   ask = return "/" -newtype GroundedUrl a = GroundedUrl { runGroundedUrl :: T.Text -> a }+instance MonadIO m => MonadIO (GroundedUrlT a m) where+  liftIO = lift . liftIO -instance Functor GroundedUrl where+newtype GroundedUrl h a = GroundedUrl { runGroundedUrl :: h -> a }++instance Functor (GroundedUrl h) where   fmap f x = GroundedUrl $ \a -> f $ runGroundedUrl x a -instance Applicative GroundedUrl where+instance Applicative (GroundedUrl h) where   (<*>) f x = GroundedUrl $ \a ->     runGroundedUrl f a (runGroundedUrl x a) -instance Monad GroundedUrl where+instance Monad (GroundedUrl h) where   return x = GroundedUrl $ const x   m >>= f = GroundedUrl $ \a ->     (\y -> runGroundedUrl (f y) a) (runGroundedUrl m a) -instance MonadReader T.Text GroundedUrl where+instance IsString a => MonadReader a (GroundedUrl a) where   ask = return "/"    -newtype AbsoluteUrlT m a = AbsoluteUrlT { runAbsoluteUrlT :: T.Text -> m a }+newtype AbsoluteUrlT h m a = AbsoluteUrlT { runAbsoluteUrlT :: h -> m a } -instance Functor f => Functor (AbsoluteUrlT f) where+instance Functor f => Functor (AbsoluteUrlT h f) where   fmap f x = AbsoluteUrlT $ \a ->     fmap f (runAbsoluteUrlT x a) -instance Applicative f => Applicative (AbsoluteUrlT f) where+instance Applicative f => Applicative (AbsoluteUrlT h f) where   (<*>) f x = AbsoluteUrlT $ \a ->     (<*>) (runAbsoluteUrlT f a) (runAbsoluteUrlT x a) -instance Monad m => Monad (AbsoluteUrlT m) where+instance Monad m => Monad (AbsoluteUrlT h m) where   return x = AbsoluteUrlT $ const $ return x   m >>= f = AbsoluteUrlT $ \a ->     runAbsoluteUrlT m a >>= (\x -> runAbsoluteUrlT (f x) a) -instance MonadTrans AbsoluteUrlT where+instance MonadTrans (AbsoluteUrlT h) where   lift m = AbsoluteUrlT (const m) -instance Monad m => MonadReader T.Text (AbsoluteUrlT m) where+instance ( Monad m+         , IsString a ) => MonadReader a (AbsoluteUrlT a m) where   ask = AbsoluteUrlT return -newtype AbsoluteUrl a = AbsoluteUrl { runAbsoluteUrl :: T.Text -> a }+instance MonadIO m => MonadIO (AbsoluteUrlT a m) where+  liftIO = lift . liftIO -instance Functor AbsoluteUrl where+newtype AbsoluteUrl h a = AbsoluteUrl { runAbsoluteUrl :: h -> a }++instance Functor (AbsoluteUrl h) where   fmap f x = AbsoluteUrl $ \a -> f $ runAbsoluteUrl x a -instance Applicative AbsoluteUrl where+instance Applicative (AbsoluteUrl h) where   (<*>) f x = AbsoluteUrl $ \a ->     runAbsoluteUrl f a (runAbsoluteUrl x a) -instance Monad AbsoluteUrl where+instance Monad (AbsoluteUrl h) where   return x = AbsoluteUrl $ const x   m >>= f = AbsoluteUrl $ \a ->     (\y -> runAbsoluteUrl (f y) a) (runAbsoluteUrl m a) -instance MonadReader T.Text AbsoluteUrl where+instance IsString a => MonadReader a (AbsoluteUrl a) where   ask = AbsoluteUrl id
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
urlpath.cabal view
@@ -1,5 +1,5 @@ Name:                   urlpath-Version:                0.0.6+Version:                0.1 Author:                 Athan Clark <athan.clark@gmail.com> Maintainer:             Athan Clark <athan.clark@gmail.com> License:                MIT@@ -9,26 +9,33 @@ Description:   Simple URL DSL for Haskell.   .-  Use raw combinators (kinda useless) ...+  This library tries to make it easier for people to write Url strings, +  structurally. Packages like <https://hackage.haskell.org/package/yesod-routes Yesod Routes>+  do a wonderful job at implementing string-free routing and +  references, but sometimes we have to compromise. This tries to make that +  compromise less painful.   .-  >  render $ "foo.php" <?> ("key1","bar") <&> ("key2","baz")+  Use bare combinators to render your strings (kinda useless):+  .+  >  expandRelative $ "foo.php" <?> ("key1","bar") <&> ("key2","baz")   >   >  ↪ "foo.php?key1=bar&key2=baz"   .-  ... or use the MonadReader instance for a configurable root ...+  ... or use the MonadReader instance for a configurable host:   .-  >  let url = runReader $ expandAbsolute $ "foo.php" <?> ("key1","bar") <&> ("key2","baz")-  >  url "example.com"+  >  let path = runAbsoluteUrl $ url $ "foo.php" <?> ("key1","bar") <&> ("key2","baz")+  >  path "example.com"   >   >  ↪ "example.com/foo.php?key1=bar&key2=baz"   .-  ... in Lucid ...+  @url@ puts the @UrlString@ in a MonadReader that we can use for applying our +  host. We use different monads for different deployment schemes (currently we +  have 3 - @RelativeUrl@, @GroundedUrl@, and @AbsoluteUrl@), which we can +  integrate in different libraries, like Lucid:   .-  >  (runReader $ renderTextT $-  >    (\a -> do-  >      foo <- lift $ expandAbsolute a+  >  (runAbsoluteUrl $ renderTextT $ do+  >      foo <- lift $ url $ "foo" <?> ("bar","baz")   >      script_ [src_ foo] "" )-  >    ("foo" <?> ("bar","baz"))   >  ) "example.com"   >   >  ↪ "<script src=\"example.com/foo?bar=baz\"></script>"@@ -42,18 +49,27 @@   >      run   >     >    where-  >      rootConf = flip runReaderT "http://example.com"+  >      rootConf = flip runAbsoluteT "http://example.com"   >     >      run :: ( MonadIO m-  >             , MonadReader T.Text m ) =>+  >             , MonadReader T.Text m+  >             , Url T.Text m ) =>   >             ScottyT LT.Text m ()   >      run = get "/" $ do-  >        path <- lift $ expandAbsolute $ "foo" <?> ("bar","baz")+  >        path <- lift $ url $ "foo" <?> ("bar","baz")   >        text $ LT.fromStrict path   >   >  λ> curl localhost:3000/   >  ↪ "http://example.com/foo?bar=baz"-  +  .+  Note that in the scotty example, we don't use one of our deployment schemes - +  this is because the @scottyT@ function expects it's underlying monad to be an +  instance of @MonadIO@, which we can only instantiate in our monad transformers.+  .+  Please take mind - the string type underlying the Url rendering is generalized +  to @Data.String.IsString@ for convenient use with @-XOverloadedStrings@. However, +  due to that generality, we need to specify the monomorphic type (like +  @Data.Text.Text@ above).  Cabal-Version:          >= 1.10 Build-Type:             Simple@@ -64,7 +80,23 @@   GHC-Options:          -Wall   Exposed-Modules:      UrlPath                         UrlPath.Types+  --                      UrlPath.Foo   Build-Depends:        base >= 4 && < 5+                      , mtl+                      , text+                      , transformers++Test-Suite spec+  Type:                 exitcode-stdio-1.0+  Default-Language:     Haskell2010+  Hs-Source-Dirs:       src+                      , test+  Ghc-Options:          -Wall+  Main-Is:              Spec.hs+  Build-Depends:        base+                      , hspec+                      , QuickCheck+                      , quickcheck-instances                       , mtl                       , text                       , transformers