Choosing a Monad¶
So far, all our handlers used the Handler
type which in turn used the Router
monad. But this won't work if
you want to use your own monad transformer stack or an algebraic effect system. How do we solve that?
Type of Handlers¶
The handler and middleware types are defined as:
type Handler' m req a = Kleisli m (Linked req Request) (Response a)
type Handler req a = Handler' Router req a
type Middleware' m req req' a' a = Handler' m req' a' -> Handler' m req a
type Middleware req req' a' a = Middleware' Router req req' a' a
You can use any monad of your choice m
with a Handler'
or Middleware'
. However, m
must be an
instance of MonadRouter
to make use of the routing features. One option is to implement an instance of MonadRouter
for your monad. However, it'd be a lot easier if there was a way to transform a handler in your monad to a
handler in the Router
monad.
Transformations¶
There is a function for that!
transform :: (forall x. m x -> Router x)
-> (Handler' m req a -> Handler' Router req a)
transform = ...
You provide a function that can translate values in your monad m
to values in Router
. And now you can
translate handlers in your custom monad to handlers in Router
monad. Isn't that handy?
For example, if you want to use the getWidget
route from the previous section with your own monad stack, this is
what you would do:
-- This is our monad stack
type MyMonad a = ReaderT env IO a
getWidget :: Handler' Router req a
getWidget = method @GET
$ path @"/v1/widgets"
$ pathVar @"widgetId" @Int
$ pathEnd
$ handler getWidgetHandler
getWidgetHandler :: Has (PathVar "widgetId" Int) req => Handler' MyMonad req a
getWidgetHandler = ...
handler :: Handler' MyMonad req a -> Handler' Router req a
handler = transform readerToRouter
readerToRouter :: MyMonad a -> Router a
readerToRouter = liftIO . flip runReaderT env