Создание экземпляра MonadBaseControl для newtype

Предположим, что у меня есть простое объявление newtype

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

Я хочу создать экземпляр Foo из MonoBaseControl IO. Это должно быть легко, поскольку ReaderT Int IO уже является экземпляром ввода-вывода MonadBaseControl. Однако автоматическое получение его с помощью GeneralizedNewtypeDeriving не работает, потому что класс MonadBaseControl имеет связанный тип.

Как написать экземпляр ввода-вывода MonadBaseControl для Foo? defaultLiftBaseWith и defaultRestoreM должны быть полезны, но немного сложно расшифровать их типы.

Ответ 1

Foo не является "базовой" монадой или монадным трансформатором. defaultLiftBaseWith здесь не будет полезен, так как вы хотите, чтобы экземпляр для Foo был идентичен экземпляру для ReaderT Int IO.

Сначала используйте GND для получения скучных экземпляров:

{-# 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)

Экземпляр для MonadBaseControl IO просто удаляет новый тип, использует функции из экземпляра ReaderT и возвращает результат в newtype:

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

Обратите внимание, что если StM не было ассоциированным типом семейства, вы могли бы сделать что-то вроде

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

type instance StM Foo a = a