1

この便利な表記法で書かれているので、これは私にはかなり明白に思えます。でも、砂糖を抜いたバージョンを使って動作させることができないの>>=が気になります。誰かがこれらを拡張表記で書き直してもらえますか?

ネストされていません

stack1 :: StateT Int Identity ( Int, Int )
stack1 = do
    a <- get 
    put ( a + 1 )
    b <- get
    return ( a, b )

runstack1 :: ( Int, Int )
runstack1 = evalState stack1 11

ネスト

stack3 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack3 = do
    modify (+10)
    lift $ modify ( ++ " world" )
    lift . lift $ modify ( ++ " word" )
    a <- get
    b <- lift get
    c <- lift . lift $ get
    return ( a, b, c )


runStack3 :: ( Int, String, String )
runStack3 = runIdentity $ evalStateT ( evalStateT ( evalStateT runStack3 1 ) "hello" ) "harro"

さらに、runStack1とrunStack3の署名を比較することで、なぜ必要なのかがわかりますrunIdentityが、stack1とstack3の両方がIdentityコンストラクターをラップしているため、なぜこれが必要なのかを誰かが説明できますか?

4

1 に答える 1

4

ネストされた例の脱糖バージョン:

stack4 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack4 = modify (+10) >>= \_ ->
         (lift $ modify ( ++ " world" )) >>= \_ ->
         (lift . lift $ modify ( ++ " word" )) >>= \_ ->
         get >>= \a ->
         lift get >>= \b -> 
         (lift . lift $ get) >>= \c ->
         return (a,b,c)

適用可能なスタイルで:

import Control.Applicative

stack5 :: StateT Int ( StateT String ( StateT String Identity ) ) ( Int, String, String )
stack5 = modify (+10) *> 
         (lift $ modify ( ++ " world" )) *>
         (lift . lift $ modify ( ++ " word" )) *>  
         ((,,) <$> get <*> lift get <*> (lift . lift $ get))

また、Lambdabotは自動脱糖を実行できます。この質問を参照してください。

の必要性に関してはrunIdentity、それについて不思議なことは何もありません。内部の値を取得するには、モナドスタックのすべてのレイヤーをアンラップする必要があり、Identityたまたまスタック内にあります。これで、モナドはとStateの観点から実装できますが、その場合、ユーザーには内部の機械を隠す「統一されたビュー」が表示されます。パッケージ内のソースコードを確認すると、内部で呼び出されていることがわかります。StateTIdentityrunStatetransformersrunIdentity

于 2013-03-23T15:23:21.557 に答える