Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating MonadBaseControl instance for newtype

Suppose I have simple newtype declaration

newtype Foo a = Foo { unFoo :: ReaderT Int IO a }

I want to make Foo instance of MonadBaseControl IO. It should be easy, since ReaderT Int IO is already an instance of MonadBaseControl IO. However, automatically deriving it using GeneralizedNewtypeDeriving doesn't work, because MonadBaseControl class has an associated type.

How can one write a MonadBaseControl IO instance for Foo? defaultLiftBaseWith and defaultRestoreM should be helpful, but it's a bit hard to decipher their types.

like image 381
xyzzyz Avatar asked Jan 25 '15 15:01

xyzzyz


1 Answers

Foo is neither a "base" monad, nor a monad transformer. defaultLiftBaseWith won't be helpful here, since you want the instance for Foo to be identical to the one for ReaderT Int IO.

First, use GND to get the boring instances:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Control.Monad.Trans.Control
import Control.Monad.Base 
import Control.Monad.Reader 
import Control.Applicative

newtype Foo a = Foo { unFoo :: ReaderT Int IO a } 
  deriving (Monad, Applicative, Functor, MonadBase IO)

The instance for MonadBaseControl IO just removes the newtype, uses the functions from the ReaderT instance, and puts the result back in the newtype:

instance MonadBaseControl IO Foo where
  type StM Foo a = a 
  liftBaseWith f = Foo $ liftBaseWith $ \q -> f (q . unFoo)
  restoreM = Foo . restoreM 

Note that if StM wasn't an associated type family, you could do something like

newtype Foo a = Foo { unFoo :: ReaderT Int IO a } 
  deriving (Monad, Applicative, Functor, MonadBase IO, MonadBaseControl IO)

type instance StM Foo a = a 
like image 130
user2407038 Avatar answered Oct 27 '22 13:10

user2407038