2012-02-05 14 views
1

単純なモナドトランスEntityBuilderTが定義されています。これはちょうどReaderTより新しいタイプです。 ReaderTを使って単純なnewtypeラッパーにmonad-controlを使用する方法

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 

はもちろん、私はモナドの種類もはや一致としてこの機能fwithNewSourceを適用することはできません。私はそのようなコンビネータの新しいバージョンを書くためにmonad-controlを使用しようとしました。

これまでに書いたコードを以下に示します。インスタンスの定義はOKのようですが、コンパイラ(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) 

答えて

3

問題は、ベースモナドがIOあるので、runMaybeT (EntityBuilderT IO) a -> IO (StM (MaybeT (EntityBuilderT IO) a))型を持つが、あなたはEntityBuilderT IOアクションとして、その戻り値を使用している、ということです。また、controlに渡す関数の戻り値は、EntityBuilderT IOではなくIOでなければなりません。

これは、MonadBaseControlインスタンスが、変換されたモナドの基本モナドに物を持ち上げると言っているためです。m; MaybeT (EntityBuilderT IO)の基底がIOであるので、controlは、RunInBase (MaybeT (EntityBuilderT IO)) IOからIO (StM (MaybeT (EntityBuilderT IO)) a)までの関数をとる。

残念ながら、私は解決策を示唆するのにモナドコントロールを十分に経験していません。おそらくMaybeTMonadTransControlインスタンスを使用して "1レベル下"機能を実現できますか?

+0

あなたの説明は非常に意味があります。ただし、コードはまだです。TypeCheckに失敗: は MonadBaseControl IO IO、依存関係から生じる を組み合わせるために、機能の依存関係を使用して 'M「' EntityBuilderT M0' でタイプ 'IOと一致しませんでした - 中> B」 - > withNewSourceのitemId( 制御の$ \ラン:式で制御 :Control.Monad.Trans.Control ' MonadBaseControl(EntityBuilderT M0)IO、 'コントロールの使用に起因 ' 式で 'でインスタンス宣言liftBase(run m)) – akborder

+0

申し訳ありません!私は私の答えを更新しました。ちなみに、[backticks](http://meta.stackexchange.com/questions/55437/how-can-the-backtick-character-be-included-in-code)を使用して、バッククォートを含むコードの書式を設定することができます。 – ehird

関連する問題