I'am trying to figure out how to implement an instance of MonadBaseControl for type Foo which is a newtype wrapper around a StateT instance. You would think it would be implemented just like this but that does not seem to be the case. I'm assuming the state piece is causing the issue here, so is there a way to drop it?
Code:
newtype Foo a = Foo { unFoo :: StateT Int IO a }
deriving (Monad, Applicative, Functor, MonadBase IO)
instance MonadBaseControl IO Foo where
type StM Foo a = a
liftBaseWith f = Foo $ liftBaseWith $ \q -> f (q . unFoo)
restoreM = Foo . restoreM
Error:
Couldn't match type ‘a’ with ‘(a, Int)’
‘a’ is a rigid type variable bound by
the type signature for restoreM :: StM Foo a -> Foo a
Expected type: a -> StateT Int IO a
Actual type: StM (StateT Int IO) a -> StateT Int IO a
Relevant bindings include
restoreM :: StM Foo a -> Foo a
In the second argument of ‘(.)’, namely ‘restoreM’
In the expression: Foo . restoreM
To avoid UndecidableInstances
, the linked answer expanded a type family that, for human readability, it really shouldn't have. Namely, he writes
instance MonadBaseControl IO Foo where
type StM Foo a = a
when instead one might consider writing
instance MonadBaseControl IO Foo where
type StM Foo a = StM (ReaderT Int IO) a
to make it more clear how to choose the correct right-hand side for a given newtype wrapping. With the analogous change (and UndecidableInstances
), your code works fine. If you want to avoid UndecidableInstances
, you can do the same expansion done in the linked answer; an example of asking ghci what the expansion should be looks like this:
> :kind! forall a. StM (StateT Int IO) a
forall a. StM (StateT Int IO) a :: *
= (a, Int)
So for the StateT
version of Foo
we could also write:
instance MonadBaseControl IO Foo where
type StM Foo a = (a, Int)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With