Как использовать monad-control для простой оболочки нового типа над ReaderT - PullRequest
1 голос
/ 05 февраля 2012

Я определил простой монадный преобразователь EntityBuilderT, который является просто новым типом над ReaderT.

data EntityBuilderState = ...

newtype EntityBuilderT m a = EntityBuilderT (ReaderT EntityBuilderState m a)

Чтобы обернуть функцию в новую "среду", я написал следующеекомбинатор:

withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId builder = ...

В некоторых случаях я также хочу построить больший стек трансформаторов.Например:

f :: MaybeT (EntityBuilderT m) a

Очевидно, я не могу применить withNewSource к этой функции f, поскольку типы монад больше не совпадают.Поэтому я попытался использовать monad-control для написания новой версии такого комбинатора.

Код, который я написал до сих пор, показан ниже.Хотя с определениями экземпляра все в порядке, компилятор (GHC 7.4.1) отклоняет код со следующим сообщением:

   Couldn't match type `IO' with `EntityBuilderT m0'
    When using functional dependencies to combine
      MonadBaseControl IO IO,
        arising from the dependency `m -> b'
        in the instance declaration in `Control.Monad.Trans.Control'
      MonadBaseControl (EntityBuilderT m0) IO,
        arising from a use of `control'
    In the expression: control
    In the expression: control $ \ run -> withNewSource itemId (run m)

Я несколько растерялся.Кто-нибудь понимает, в чем проблема на самом деле?


{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving,
             MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}

import Control.Applicative (Applicative)
import Control.Monad (liftM)
import Control.Monad.Base
import Control.Monad.Trans (MonadTrans)
import Control.Monad.Trans.Control
import Control.Monad.Trans.Maybe (MaybeT)
import Control.Monad.Trans.Reader (ReaderT, withReaderT)


data EntityBuilderState

newtype EntityBuilderT m a = EntityBuilderT { unEB :: ReaderT EntityBuilderState m a }
  deriving (Applicative, Functor, Monad, MonadTrans)

instance MonadBase b m => MonadBase b (EntityBuilderT m) where
    liftBase = liftBaseDefault

instance MonadTransControl EntityBuilderT where
    newtype StT EntityBuilderT a = StEB { unStEB :: StT (ReaderT EntityBuilderState) a }
    liftWith f = EntityBuilderT $ liftWith $ \run ->
                   f $ liftM StEB . run . unEB
    restoreT = EntityBuilderT . restoreT . liftM unStEB

instance MonadBaseControl b m => MonadBaseControl b (EntityBuilderT m) where
    newtype StM (EntityBuilderT m) a = StMT { unStMT :: ComposeSt EntityBuilderT m a }
    liftBaseWith = defaultLiftBaseWith StMT
    restoreM     = defaultRestoreM   unStMT


withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId (EntityBuilderT m) = EntityBuilderT (withReaderT undefined m)

withNewSource' :: String -> MaybeT (EntityBuilderT IO) a -> MaybeT (EntityBuilderT IO) a
withNewSource' itemId m = control $ \run -> withNewSource itemId (run m)

1 Ответ

3 голосов
/ 05 февраля 2012

Проблема в том, что, поскольку базовая монада IO, run имеет тип MaybeT (EntityBuilderT IO) a -> IO (StM (MaybeT (EntityBuilderT IO) a)), но вы используете ее возвращаемое значение в качестве действия EntityBuilderT IO.Кроме того, возвращаемое значение функции, которую вы передаете control, должно быть в IO, а не EntityBuilderT IO.

Это потому, что ваш экземпляр MonadBaseControl говорит, что вы поднимаете вещи в базовую монадупреобразованная монада m;поскольку основание MaybeT (EntityBuilderT IO) равно IO, control принимает функцию от RunInBase (MaybeT (EntityBuilderT IO)) IO до IO (StM (MaybeT (EntityBuilderT IO)) a).

К сожалению, у меня недостаточно опыта в управлении монадой, чтобы предложить решение;возможно, вы могли бы использовать MaybeT экземпляр MonadTransControl для достижения функциональности "на один уровень вниз"?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...