Hi -

(Apologies for the long post)

I need some help integrating a typeclass based functionality within an existing API. I am using Control.Monad.Validate for my validation of objects. Here's a simple function based on this ..

debit :: forall m. (MonadReader Env m,
                    MonadValidate [Error] m) => Y.Dense "USD" -> Account -> m Account
debit amount = updateBalance ((-1) * amount)

Now I want to make a change in the API where I would like to fetch the account from some database based on account number. For this I have a typeclass as below ..

class (Monad m) => AccountDB m where
    query :: Text -> m (Maybe Account) 
.. elided details

and I am using persistent - so I have an instance of this class as follows ..

-- | Instance of AccountDB for `SqlPersistT` (which is `ReaderT SqlBackend`)
instance (MonadIO m) => AccountDB (SqlPersistT m) where
  query ano = get (AccountKey ano)

.. other details elided

My question is how can I integrate this typeclass within the above API ? I tried as yet another constraint in the function ..

debit :: forall m. (MonadReader Env m,
                    MonadValidate [Error] m,
                    AccountDB m) => Y.Dense "USD" -> Text -> m Account
debit amount accNo = do
  maybeAccount <- query accNo  
  maybe (refuteErr $ InvalidAccountNumber accNo) (updateBalance ((-1) * amount)) maybeAccount


This compiles but I don't get the proper instance of the typeclass when I run it - possibly due to the reason that the "m" cannot be the same for AccountDB as the other constraints. And I don't want to hardcode SqlPersistT in the function debit as I would like to be able to run it with other instances of the typeclass as well (example for testing I may like to test against a HashMap).

What would be an idiomatic approach to this kind of composition ?

Any help will be appreciated.

regards.


--