This question is about groundhog
or persistent
, because I believe both share the same problem.
Say I have a transformer Tr m a
that provides some functionality f :: Int -> Tr m ()
. This functionality requires database access. There are a few options I can use here, and none are satisfactory.
I could put a DbPersist
transformer somewhere inside of Tr
. Actually, I'd need to put it at the top because there are no PersistBackend
instances for standard transformers AND I'd still need to write an instance for my Tr
newtype. This already sucks because the class is far from minimal. I could also lift every db action I do.
The other option is changing the signature of f
to PersistBackend m => Int -> Tr m ()
. This would again either require a PersistBackend
instance on my Tr
newtype, or lifting.
Now here's the real issue. How do I run Tr
inside of a context that already has a PersistBackend
constraint? There's no way to share it with Tr
.
I can either do the first option and run the actual DbPersist
transformer inside of Tr
with some new connection pool (as far as I can tell there's no way to get the pool from the PersistBackend
context I'm already in), or I can do the second option and have the run function be runTr :: PersistBackend m => Tr m a -> m a
. The second option would actually be completely fine, but the problem here is that the DbPersist
, that will eventually have to be somewhere in the stack, is now under the Tr
transformer and there are no PersistBackend
instances for the standard transformers of which Tr
is made of.
What's the correct approach here? At the moment is seems that the best option is to go with a sepatare ReaderT
somewhere in the stack that provides me with the connection pool on request and then do runDbConn
with that pool everywhere where I want to access the DB. Seeing how DbPersist
basically already is just a ReaderT
I don't see the sense in having to do that.