
Adde wrote:
Do you want to mix differently typed Connections inside a single transaction? It looks like you don't, so you may well leave out existential types altogether and simply parametrize the Transaction monad on the type of the connection it uses.
data Connection c => TransactionState c = TS c data Transaction c a = Transaction (TransactionState c -> (a, TransactionState c)
instance Monad (Transaction c) where ...
getConnection :: Transaction c c ...
Note that Control.Monad.State does the same.
You are correct in that I don't want to mix different kind of connections inside a single transaction. I just didn't want to have to parameterize every single function using the monad.
doSomething :: Transaction FirebirdConnection ()
doSomethingElse :: Transaction FirebirdConnection ()
Maybe I'm misunderstanding something but as I see it that would kind of stop me from running doSomething and doSomethingElse using different kinds of databases.
Your monadic action may well be polymorphic in the state type: doSomething :: Connection c => Transaction c () doSomethingElse :: Connection c => Transaction c () thus working for all databases. In case your function really depends on a particular database vendor, you have the possibility to annotate this in the connection type: doSomethingThatOnlyWorksForFirebird :: Transaction Firebird () The additional 'Connection c' constraint in the first examples may be a bit unwieldy. But I guess that you can drop when redesigning your monad. After all, I'm unsure what you intend 'Transaction' to do. Wouldn't runTransaction :: Transaction c a -> IO a be a function you need? Shouldn't things of the Transaction monad be completely back end independent so that you can drop the parameter c altogether? Why do you intend 'Transaction' to be a state monad, I mean why is the only thing you can do with the state something of type (c -> String -> IO ())? Btw, this has been (c -> String -> Transaction ()) in your original post. Regards, apfelmus