Reputation: 317
Apologies for the potentially vague question title - I'm not sure how to phrase it because I have a pretty poor understanding of what the problem is.
Basically, how do I make the following compile? :-p
{-# LANGUAGE MultiParamTypeClasses #-}
class (Monad m) => MyClass m a where
valM :: m (Maybe a)
val :: m a
f :: (MyClass m a) => (m a -> IO a) -> IO (a, Maybe a)
f g = do
x <- g val
yM <- g valM
return (x, yM)
GHC (v8.2.2) complains that a
is a rigid type variable and can't seem to cope with the idea that (g val)
and (g valM)
could produce values of different types. I've tried using RankNTypes
but to no avail.
Is there an extension I can use to help the compiler, or is there something conceptually broken with what I'm trying to do from a type-inference point of view?
Upvotes: 2
Views: 158
Reputation: 43842
You’re right that you need RankNTypes
, but you’re missing a forall
. The correct type for f
is:
f :: MyClass m a => (forall b. m b -> IO b) -> IO (a, Maybe a)
…since the function passed to f
must work for any result type, and it shouldn’t be related to the a
in the result.
It’s also potentially worth noting that this sort of function is also known as a natural transformation, and the natural-transformation package provides a (~>)
type alias for such functions:
type (~>) f g = forall a. f a -> g a
Therefore, using that type alias, you could also write f
like this:
f :: MyClass m a => (m ~> IO) -> IO (a, Maybe a)
Upvotes: 8