その定義はあなたが望むものではないと思いSbT
ます。これはファンクターの構成m
を定義し、パラメーターがまたはFunctor
であると仮定すると、Applicative
これらのプロパティを保持する必要があります。しかし、そのような合成は、一般に、他の 2 つのモナドから新しいモナドを作成しません。その件について詳しくは、この質問を参照してください。
では、必要なモナド変換子をどのように作成するのでしょうか? モナドは直接合成しませんが、モナド変換子は合成できます。したがって、既存のトランスフォーマーから新しいトランスフォーマーを構築するには、基本的にその構成に名前を付けたいだけです。これは、トランスフォーマー スタックに渡すのではなく、直接newtype
適用しているため、持っている とは異なります。m
モナド変換子を定義する際に心に留めておくべきことの 1 つは、モナド変換子が特定の方法で必ず「逆方向」に機能することです。複合変換子をモナドに適用すると、「最も内側の」変換子が最初のクラックを取得し、変換されたモナドがそれを適用します。生成されるものは、次のトランスフォーマー出力が処理するものです。&c. これは、構成された関数を引数に適用するときに得られる順序とまったく変わらないことに注意してください。たとえば、構成内の「最初の」関数であっても、最初に(f . g . h) x
引数を与えます。h
f
さて、あなたの複合トランスフォーマーは、それが適用されてSB
いるモナドを受け取り、それを最も内側のトランスフォーマーに渡す必要があります。これがうまくいかなかったのも不思議ではありません。まず、それを削除する必要があります。それはどこにある?そうではありません-- それを削除することはできますが、削除したくありません。うーん、でも待ってください。もう一度、何と定義されていますか? そうそう:State
State
type State s = StateT s Identity
ああ、それでは行きます。そこから出しましょうIdentity
。私たちはあなたの現在の定義から行きます:
type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
同等の形式に:
type SB i a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) Identity ) a
次に、怠け者を追い出します。
type SB' i m a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a
type SB i a = SB' i Identity a
しかし、今でSB'
は疑わしいことにモナド変換子の定義のように見えますが、それには正当な理由があります。そのため、ラッパーを再作成し、newtype
そこにいくつかのインスタンスを投げます。
newtype SbT i m a = SbT { getSB :: ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a }
instance (Functor m) => Functor (SbT i m) where
fmap f (SbT sb) = SbT (fmap f sb)
instance (Monad m) => Monad (SbT i m) where
return x = SbT (return x)
SbT m >>= k = SbT (m >>= (getSB . k))
instance MonadTrans (SbT i) where
lift = SbT . lift . lift
runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t)
runSbT (SbT m) e s = runStateT (runReaderT m e) s
ここでのrunSbT
関数はフィールド アクセサーではなく、既知のスタック内のトランスフォーマーごとに構成された "実行" 関数です。同様に、lift
関数は 2 つの内部トランスフォーマーに対して 1 回持ち上げてから、最後のnewtype
ラッパーを追加する必要があります。これらはどちらも単一のモナド変換子として機能させ、実際には複合体であるという事実を隠します。
必要に応じて、構成されたトランスフォーマーのインスタンスを持ち上げることで、MonadReader
andのインスタンスを簡単に作成できます。MonadState