2013-04-30 5 views
6

一般的なテーマ:モナドを積み重ねるというアイデアは非常に魅力的ですが、コードの実行方法や層を実行するための適切な命令は何かを思い描いています。以下は、スタックの1つの例です:Writer、State、State、およびError(順序は特にありません)?モナドTスタック内の関数の実行順序についてはどう思いますか?

----------------------- 
-- Utility Functions -- 
----------------------- 

type Memory = Map String Int 
type Counter = Int 
type Log  = String 

tick :: (MonadState Counter m) => m() 
tick = modify (+1) 

record :: (MonadWriter Log m) => Log -> m() 
record msg = tell $ msg ++ "; " 

------------------ 
-- MonadT Stack -- 
------------------ 

mStack :: (MonadTrans t, MonadState Memory m, MonadState Counter (t m), MonadError ErrMsg (t m), MonadWriter Log (t m)) => t m Int 
mStack = do 
    tick 
    m <- lift get 
    let x = fromJust (M.lookup "x" m) in x 
    record "accessed memory" 
    case True of 
     True -> return 100 
     False -> throwError "false" 

エラーがスローかの機能の他の部分とは何の関係もありませんされているかどうか、mStackに注意してください。

(Right 100, 1, "accessed memory", fromList [...]) 

または一般に:

は今、理想的に私は、出力は次のようになりたい

(output of errorT, output of stateT Counter, output of writerT, output of StateT Memory) 

しかし、私はそれを動作させることはできません。

mem1 = M.fromList [("x",10),("y",5)] 
runIdentity $ runWriterT (runStateT (runStateT (runErrorT mStack) 0) mem1) "" 

しかし、このエラーメッセージ取得しています:具体的には、私はエラーが最外層にある場合と同様に、スタックを実行しようとした、一般的には、ときに私はさておき

Couldn't match type `Int' with `Map [Char] Int' 

に上記のインスタンスを呼び出しています:

runMonadT_1 (runMonadT_2 expr param2) param1

は01に関連する機能がありますが最初に実行され、その出力がmonadT_1に関連する関数にパイプされますか?言い換えれば、上記の関数mStackにコードのように必須の命令は、モダードTが実行される順序に完全に依存する実行順序です(構造内の剛性を除いて、liftによって導入されました)。

答えて

6

明示的なモナド変圧器スタックを使用して計算を入力しようとした場合は、より多くの有益なタイプのエラーを得ているでしょう:

mStack :: ErrorT String (StateT (Map String Int) (StateT Int Writer)) Int 

あなたはghc以前のタイプのエラーをキャッチしているだろう、ということ行っていました。 modifylift getの両方が起こっている:あなたは、あなたがミスをキャッチしたい、これに明示的なスタックを提供した場合

modify (+1) -- i.e. from `tick` 
... 
yourMap <- lift get 

:理由は、あなたが最上位レベルでmStack以内に次の2つのコマンドを使用することです最初のStateTレイヤーをターゲットにします。そのレイヤーは同じStateTレイヤーになります。

modifyErrorT層から始まり、それが外側StateT層に当たるまで下方に進み、外側StateTInt状態を使用しなければならないと結論します。 getは外側のStateTレイヤーから始まり、既にStateTレイヤーにあり、内部レイヤーはすべてStateTであることに注意しているため、StateTレイヤーにはMapが格納されている必要があります。

ghc「何を与えるの?このレイヤーにはIntMapの両方を格納することはできません! "というエラーメッセージが表示されますが、具体的なモナド変換スタックの代わりにタイプクラスを使用していたため、ghcはこれがあなたが具体的なスタックを指定するまで待っにおける型エラー

ソリューションは単純です:ちょうどあなたgetに別のliftを追加し、あなたが意図したように、それは今の内側StateT層を対象とします

私は個人的に避けることを好みますmtlクラスは、完全に常に、transformersライブラリだけを使用して、コンクリートモナド・トランススタックで動作します。 liftを使用してどのレイヤーを正確にする必要があるので、怒らせるのですが、路肩からの頭痛が少なくなります。

関連する問題