Existential is probably not what you want here; There is no way to "observe" the actual types associated with e
or w
in the value of Dangerous a
, so you are completely limited to the operations given to you by Error
and Show
.
In other words, the only thing you know about w
is that you can turn it into a String
, so it can just be a String
(ignoring priority to simplify things) and the only thing you know about e
is that you can turn it into a String
, you can include a String
in it, and you have a great value for it ( noMsg
). It is not possible to claim or verify that these types are the same as any other, so when you put them in Dangerous
there is no way to restore any special structure that these types can have.
What the error message says is, in fact, your type for runDangerous
claims that you can turn Dangerous
into (Either ea, [w])
for any e
and w
that have corresponding instances. This is clearly wrong: you can only turn Dangerous
into this type for one choice of e
and w
: the one with which it was created. w1
is just that your Dangerous
type is defined by a variable of type w
, as well as runDangerous
, so GHC renames one of them to avoid name conflicts.
The type you need to provide runDangerous
looks like this:
runDangerous :: (forall e w. (Error e, Show e, Show w) => (Either ea, [w]) -> r) -> Dangerous a -> r
which, given a function that takes a value of type (Either ea, [w])
for any variants of e
and w
, as long as they have the specified instances, and a Dangerous a
, returns the result of this function. It's pretty hard to plunge!
The implementation is simple as
runDangerous f (Dangerous m) = f $ runState (runErrorT m) []
what constitutes a trivial change to your version. If this works for you, great; but I doubt that an existential approach is the right way to achieve what you are trying to do.
Note that you will need {-# LANGUAGE RankNTypes #-}
to express the type runDangerous
. Alternatively, you can define another existential type of your result:
data DangerousResult a = forall e w. (Error e, Show e, Show w) => DangerousResult (Either ea, [w]) runDangerous :: Dangerous a -> DangerousResult a runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) []
and extract the result using case
, but you have to be careful, or the GHC will start complaining that you allowed e
or w
escape - which is equivalent to trying to pass an insufficiently polymorphic function to another runDangerous
form; that is, which requires more restrictions that e
and w
are outside of what the runDangerous
type runDangerous
.