2011-09-14 9 views
5

ごめんなさい質問がちょっと些細なようであれば...それは私にとってではない。変圧器としてのモナド組成の抽象化

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

も、ある、行儀モナド: は、私は喜んで、次のモナドを構成しています。 ReaderTはモナド変換子であり、StateはStateモナドであり、AlgROとAlgStateは、それぞれ可変と読取り専用の状態でパラメータ化されたデータ型です。今、私はそのことのnewtypeできちんとモナド変換子を作りたい場合は、このような何か:

newtype SbT m i a = SbT { 
    runSbT:: m (SB i a) 
} 

どのように私が進むべき? MonadTransのバインドメソッド(Monad typeclass)を組み立てることさえできません。(MonadTransの) "lift"はずっと少ないです...私は、自動導出が助けになると思いますが、この場合どのように動作するのか理解したいと思います。

ありがとうございます。

答えて

10

SbTの定義があなたの望むものだとは思いません。これは、ファンクタの構成を定義し、mパラメータがFunctorまたはApplicativeであると仮定すると、これらのプロパティは保持されます。しかし、そのような構成は、一般に、2つのうちの新しいモナドを作成しません。その件については、this questionを参照してください。

だから、どのようにを実行しますか?あなたは、あなたが望むモナドトランスを作成しますか?モナドは直接作成しませんが、モナド変圧器を構成することができます。だから、既存のトランスフォーマーから新しいトランスを構築するには、基本的にそのコンポジションに名前を付けるだけです。これはnewtypeとは異なり、mをトランススタックに渡すのではなく、直接適用しているためです。

モナド変圧器の定義については、コンポジットトランスをモナドに適用すると、「最も内側の」変圧器が最初の亀裂を取得し、それが生成する変換されたモナドは、次のトランスフォーマーが動作するようになったものです。& c。これは、作成された関数を引数に適用するときに得られる順序とまったく同じではないことに注意してください。 (f . g . h) xは、最初にhに引数を与えますが、fはコンポジションの最初の関数です。

わかりましたが、ので、あなたの複合トランスは、それが適用されますモナドを取ると、オム....おっと、SBはモナドに適用すでにであることが判明している最も内側の変圧器、にそれを渡す必要があります。これがうまくいかないのも不思議ではない。最初にそれを削除する必要があります。それはどこにある? State --we それを削除することはできますが、それはあなたが望むものの一部なので、望みたくありません。うーん、しかし、待ってください - 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ラッパーを再作成し、そこにいくつかのインスタンスを放り出す:各ここrunSbT機能は、フィールドアクセサではなく、構成「実行」機能:

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 

のノートを取るためにカップルの事を私たちが知っているスタックの変圧器。同様に、lift関数は、2つの内部トランスのために一度持ち上げてから、最後のnewtypeラッパーを追加する必要があります。これらの両方は、モナド変圧器として機能し、実際にコンポジットであることを隠しています。

希望する場合は、構成されたトランスのインスタンスを持ち上げて、MonadReaderMonadStateのインスタンスを作成するのは簡単です。

+0

これが実行されます。ありがとう! – dsign

2

あなたはあなたのnewtypeのものの周りにさらにmをラップするつもりでしたか?私は、次のことをお勧めします:書くことは、あなたのinstance Monad (Sb i)ビットを簡単にする必要があります

newtype Sb i a = Sb { runSb :: SB i a } 

...。あなたが実際にモナド変圧器を書こうとしているのであれば、変圧器を使ってください。例えば、

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
newtype SbT m i a = SbT { runSbT :: SBT m i a } 

は、関心のある第二の点として、それは(それらは常に「完全に適用され」なければならないので)type同義語η-低減することがしばしば望ましいです。 SBSBTでこれを行うと、次のようになります。

type SB i = ReaderT (AlgRO i) (State (AlgState i)) 
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m) 
+0

ところで、あなたはそのように 'SbT'からモナド変圧器を作ることはできません。トランスフォーマーは、(* - > *) - > * - > * 'のように、モナドとタイプを引数として取ります。 'i'パラメータは最初になければならないので、部分的に適用することができます。 –

関連する問題