Search code examples
haskellhaskell-snap-framework

Could not deduce (CI.MonadCatchIO (Handler App (AuthManager App))) in Snap handler


I'm using an openapi-based library within a handler; unfortunately it throws a lot of network or http exceptions, so I'm attempting to use Control.Monad.CatchIO with it, but banging my head against the types.

This is where I'm at:

{-# LANGUAGE ScopedTypeVariables #-}

import qualified Control.Exception as E
import qualified Control.Monad.CatchIO as CI (try,MonadCatchIO)
import           Control.Monad.State

import           Snap
import           Application
import           Services
import qualified Helper.Heist as H
import           Snap.AzureAdAuth

-- this is the function that throws the exceptions
runQuery :: (Produces req accept, MimeUnrender accept b1, Show b1, MimeType contentType) => ServicesRequest req contentType b1 accept -> Handler App (AuthManager App) (Either MimeError b1)
runQuery r = ...


runQuery' :: forall req accept contentType b1. (Produces req accept, MimeUnrender accept b1, Show b1, MimeType contentType)
                       => ServicesRequest req contentType b1 accept
                       -> Handler App (AuthManager App) (Either E.SomeException (Either MimeError b1))
runQuery' req = 
  CI.try $ runQuery req :: Handler App (AuthManager App) (Either E.SomeException (Either MimeError b1))

Compiling gives this error message:

    * Could not deduce (CI.MonadCatchIO
                          (Handler App (AuthManager App)))
        arising from a use of `CI.try'
      from the context: (Produces req accept, MimeUnrender accept b1,
                         Show b1, MimeType contentType)
        bound by the type signature for:
                   runQuery' :: forall req accept contentType b1.
                                       (Produces req accept, MimeUnrender accept b1, Show b1,
                                        MimeType contentType) =>
                                       ServicesRequest req contentType b1 accept
                                       -> Handler
                                            App
                                            (AuthManager App)
                                            (Either E.SomeException (Either MimeError b1))
        at src/Helper/API.hs:(96,1)-(99,102)
    * In the expression: CI.try $ runQuery req
      In an equation for runQuery':
          runQuery' req = CI.try $ runQuery req

Any thoughts on how to line up the types?


Solution

  • I can't reproduce your example exactly, because there are still many types for which I don't know where they're coming from.

    The Snap framework's Handler monad uses MonadBaseControl to embed the IO monad, rather than the more traditional mtl-style MonadIO and MonadCatchIO that MonadCatchIO-mtl uses.

    Instead, the lifted-base package provides primitives such as try (in Control.Exception.Lifted) for these embeddings.

    The following code compiles for me:

    import Snap.Snaplet
    import Control.Exception.Lifted hiding (Handler)
    
    runQuery :: Handler a b c
    runQuery = undefined
    
    runQuery' :: (Exception e) => Handler a b (Either e a1)
    runQuery' = try runQuery
    

    Happy Haskelling!