5

質問が少し些細なことのように思われる場合は申し訳ありません...それは私のためではありません。私は喜んで次のモナドを作成しました:

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

これは、よく振る舞うモナドです。ReaderT はモナド変換子であり、State は State モナドであり、AlgRO と AlgState は、それぞれ変更可能な状態と読み取り専用の状態を表す i でパラメータ化されたデータ型です。さて、それを newtype を持つきちんとしたモナド変換子にしたい場合は、次のようになります:

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

どうすればいいですか?bind メソッド (Monad 型クラスの) をまとめることさえできず、ましてや "lift" (MonadTrans の) をまとめることさえできません... 自動導出が役立つと思いますが、この場合にどのように機能するかを理解したいと思います。

前もって感謝します。

4

2 に答える 2

10

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

では、必要なモナド変換子をどのように作成するのでしょうか? モナドは直接合成しませんが、モナド変換子は合成できます。したがって、既存のトランスフォーマーから新しいトランスフォーマーを構築するには、基本的にその構成に名前を付けたいだけです。これは、トランスフォーマー スタックに渡すのではなく、直接newtype適用しているため、持っている とは異なります。m

モナド変換子を定義する際に心に留めておくべきことの 1 つは、モナド変換子が特定の方法で必ず「逆方向」に機能することです。複合変換子をモナドに適用すると、「最も内側の」変換子が最初のクラックを取得し、変換されたモナドがそれを適用します。生成されるものは、次のトランスフォーマー出力が処理するものです。&c. これは、構成された関数を引数に適用するときに得られる順序とまったく変わらないことに注意してください。たとえば、構成内の「最初の」関数であっても、最初に(f . g . h) x引数を与えます。hf

さて、あなたの複合トランスフォーマーは、それが適用されてSBいるモナドを受け取り、それを最も内側のトランスフォーマーに渡す必要があります。これがうまくいかなかったのも不思議ではありません。まず、それを削除する必要があります。それはどこにある?そうではありません-- それを削除することはできますが、削除したくありません。うーん、でも待ってください。もう一度、何と定義されていますか? そうそう:StateState

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ラッパーを追加する必要があります。これらはどちらも単一のモナド変換子として機能させ、実際には複合体であるという事実を隠します。

必要に応じて、構成されたトランスフォーマーのインスタンスを持ち上げることで、MonadReaderandのインスタンスを簡単に作成できます。MonadState

于 2011-09-14T14:06:07.680 に答える
2

mあなたはあなたのニュータイプで物事の周りに追加のものを包むつもりでしたか?私は次のことを提案します:

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

...これでinstance Monad (Sb i)少し書きやすくなります。あなたが本当にモナド変換子を書き込もうとしているのなら、あなたはずっと下の変換子を使うべきです。例えば、

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

2番目の興味深い点として、type同義語をη-reduceする方が望ましい場合がよくあります(常に「完全に適用」する必要があるため)。でこれを行うとSBSBT次のようになります。

type SB i = ReaderT (AlgRO i) (State (AlgState i))
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m)
于 2011-09-14T14:06:42.773 に答える