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?
CodePudding user response:
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.