Skip to content

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