
Adde:
data TransactionT = forall c. (Connection c) => TransactionT c
data Transaction a = Transaction (TransactionT -> (a, TransactionT))
getConnection :: Transaction c getConnection = Transaction (\t@(TransactionT c) -> (c, t))
class Connection c where connectionExecute :: c -> String -> Transaction ()
execute :: String -> Transaction () execute s = connectionExecute getConnection s
I'm assuming you've read the GHC user's guide on existentially quantified data constructors: http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html... When you wrap a value in an existentially quantified data constructor, the concrete type of the value is erased. Although the wrapped value does have a concrete type, "the compiler" has forgotten it. This is what the "existential" part means: you know the type exists, but that's about all you know. In this case, you've used a context to restrict the existential quantification, so at least you know the type conforms to the Connection class. Since the concrete type has been forgotten, there's no way to get it back. You can't write a function that exposes the forgotten type, so getConnection is basically a lost cause. When you write "getConnection :: Transaction c", you are saying that the function is fully polymorphic in c. In other words, you are allowing the caller of getConnection to specify the type of connection. But you can't do that, because you already gave the connection a concrete type when you constructed the TransactionT. You might think you could get away with "getConnection :: Connection c => Transaction c", but this is still too polymorphic. So what can you do? You can pattern-match on a TransactionT, provided you don't allow the existentially-quantified type to escape the scope of the pattern-match. In this case, all you know is that the type conforms to the Connection class, so you must use methods from the Connection class within that scope to consume the quantified type. Now, I'm a little confused by the circular dependency between TransactionT, Transaction and the Connection class, so I'll use a slightly different example:
class Connection c where connectionExecute :: c -> String -> IO ()
Now, you can write a corresponding function for a TransactionT (not tested):
transactionExecute :: TransactionT -> String -> IO () transactionExecute (TransactionT c) s = connectionExecute c s
Note that the existentially-quantified type variable is not exposed in the signature of transactionExecute, because it has been consumed by connectionExecute within the scope of the TransactionT pattern-match. Hope that helps.