Search code examples
haskelltypeclassyesod

Cannot represent equality constraint as a custom constraint


I have a function type declaration

f :: MonadHandler m => SqlPersistT m ()

Which I want to convert to

f :: MonadHandlerDB m => m ()

I try everything I can think of to define constraint MonadHandlerDB, but cannot get either it or function type declaration to compile, e.g.:

class (forall a . (MonadHandler m, m ~ SqlPersistT a)) => MonadHandlerDB m

class MonadHandlerDB m
instance MonadHandler a => MonadHandlerDB (SqlPersistT a)

type MonadHandlerDB m = forall a . (MonadHandler a, m ~ SqlPersistT a)

type MonadHandlerDB = forall a . (MonadHandler a => m ~ SqlPersistT a)

type MonadHandlerDB m = forall a . (MonadHandler a => m ~ SqlPersistT a)

One of the errors:

Couldn't match type `m' with `ReaderT backend0 m0
`m' is a rigid type variable bound by
the type signature for:
  f:: forall (m :: * -> *).
      MonadHandlerDB m =>
      m ()

SqlPersistT is defined as

type SqlPersistT = ReaderT SqlBackend

How do I express this constraint?


Solution

  • I think this achieves what you want:

    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE KindSignatures #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE TypeSynonymInstances #-}
    
    import Control.Monad.Trans.Reader (ReaderT (ReaderT))
    import Database.Persist.Sql (SqlPersistT)
    import Yesod.Core (MonadHandler)
    
    f :: MonadHandlerDB m => m ()
    f = undefined
    
    class (MonadHandler (Sub m), m ~ SqlPersistT (Sub m)) => MonadHandlerDB m where
      type Sub m :: * -> *
    
    instance MonadHandler m => MonadHandlerDB (SqlPersistT m) where
      type Sub (SqlPersistT m) = m
    

    But note that I think this is really not very good to use in practice. It makes it seem as if the m is completely polymorphic, but, in fact, it can only ever be some monad inside SqlPersistT.

    Constraints are powerful, but I think a constraint like this has a high potential to confuse its users.