2016-05-06 12 views
3
に1つのステートタイプからの移行

のは、我々はこのような最も外側の変圧器などの状態モナド変換子とモナドのスタックを持っているとしましょう:状態モナド:別

-- | SEWT: Composition of State . Except . Writer monad transformers in that 
-- order where Writer is the innermost transformer. 
-- the form of the computation is: s -> (Either e (a, s), w) 
newtype SEWT s e w m a = SEWT { 
    _runSEWT :: StateT s (ExceptT e (WriterT w m)) a } 
    deriving (Functor, Applicative, Monad, 
       MonadState s, MonadError e, MonadWriter w) 

-- | 'runSEWT': runs a 'SEWT' computation given an initial state. 
runSEWT :: SEWT s e w m a -> s -> m (Either e (a, s), w) 
runSEWT ev e = runWriterT $ runExceptT $ runStateT (_runSEWT ev) e 

私たちは、その後、いくつかの中で、やりたいです書式:SEWT s e w m a -> s -> SEWT t e w m a(>>=)またはdoブロックを使用することはもちろん不可能です。sの状態のモナドは、tのモナドと同じモナドではないためです。

私は、このようなものを想起させることができます。

-- | 'sewtTransition': transitions between one 'SEWT' computation with state s, 
-- to another with state s. The current state and result of the given 
-- computation is given to a mapping function that must produce the next 
-- computation. The initial state must also be passed as the last parameter. 
transitionState :: (Monad m, Monoid w) => ((a, s) -> SEWT t e w m a) 
       -> m (SEWT s e w m a) -> s -> m (SEWT t e w m a) 
transitionState _trans _comp _init = do 
    (res, logs) <- _comp >>= flip runSEWT _init 
    return $ do tell logs 
       case res of Left fail -> throwError fail 
          Right succ -> _trans succ 

-- 'withState': behaves like 'transitionState' but ignores the state of 
-- the first computation. 
withState :: (Monad m, Monoid w) 
      => m (SEWT s e w m a) -> s -> m (SEWT t e w m a) 
withState = transitionState $ return . fst 

は、しかし、別の状態の種類を移動するために、よりエレガントかつ一般的な方法は、おそらくありますか?

私は、2番目の計算が最初の計算の最終状態(結果のみ)に依存しないソリューションと、それが存在するソリューションの両方に興味があります。

EDIT1:改善された遷移関数:

transSEWT :: Functor m => (((a, y), x) -> (a, y)) -> SEWT x e w m a -> x -> SEWT y e w m a 
transSEWT f x_c x_i = SEWT $ StateT $ \y_i -> ExceptT . WriterT $ 
    first ((\(a, x_f) -> f ((a, y_i), x_f)) <$>) <$> runSEWT x_c x_i 

changeSEWT :: Functor m => SEWT x e w m a -> x -> SEWT y e w m a 
changeSEWT = transSEWT fst 

transS :: Monad m => (((a, y), x) -> (a, y)) -> StateT x m a -> x -> StateT y m a 
transS f x_c x_i = StateT $ \y_i -> do (a, x_f) <- runStateT x_c x_i 
             return $ f ((a, y_i), x_f) 

changeS :: Monad m => StateT x m a -> x -> StateT y m a 
changeS = transS fst 
+0

他の質問と一緒にリンクする:http://stackoverflow.com/questions/28690448/what-is-indexed-monad – Centril

答えて

6

あなたの考えはインデックス化状態モナドで実現することができます。

newtype IState i o a = IState { runIState :: i -> (o, a) } 

タイプIState i o aの値は、プロセス中oiから暗黙的な状態の種類を変換する、タイプaの値を返すステートフルな計算です。あなたは、その状態の種類を変更することはできません通常のStateモナド、とは対照的:

type State s = IState s s 

シーケンスインデックス化状態のモナドは、入力と出力が並ぶことを確認する必要があります。 1つの計算の出力タイプは、次の入力のタイプです。 Atkeyのparameterised monad(より一般的にはインデックス付きモナドとして知られています)は、有向グラフのパスを記述するモナドのようなもののクラスです。インデックス付きのモナドをバインド

class IMonad m where 
    ireturn :: a -> m i i a 
    (>>>=) :: m i j a -> (a -> m j k b) -> m i k b 

(>>>) :: IMonad m => m i j a -> m j k b -> m i k b 
mx >>> my = mx >>>= const my 

は、ドミノをプレイするようなものです:あなたはiからjに取得する方法とjからkに取得する方法を持っている場合、>>>=から行くより大きな計算に一緒にあなたのドミノを接着しますi~k。 McBrideはこのインデックスモナドのより強力なバージョンをKleisli Arrows of Outrageous Fortuneに記載していますが、これは私たちの目的には十分です。

私が上で説明したように、ドミノのようなシーケンシングは、入力と出力の位置合わせを必要とするインデックス付きモナドに必要なものです。

instance IMonad IState where 
    ireturn x = IState $ \s -> (s, x) 
    IState f >>>= g = IState $ \i -> let (o, x) = f i 
            in runIState (g x) o 

インデックスされた状態のモナドから値を取得しても、状態のタイプは変更されません。

get :: IState s s s 
get = IState $ \s -> (s, s) 

インデックスされた状態に値を入れると、古い状態が破棄されます。つまり、入力状態のタイプは好きなものにすることができます。

put :: s -> IState i s() 
put x = IState $ \_ -> (x,()) 

すべてのコードがIStateで動作するようにという注意がStateとまったく同じです!よりスマートになったのはそれだけです。

単純なIStateの計算では、タイプがIntであると予想し、状態をStringに変更し、ブール解答を返します。これらのすべてが静的にチェックされます。

myStateComputation :: IState Int String Bool 
myStateComputation = 
    -- poor man's do notation. You could use RebindableSyntax 
    get    >>>= \s -> 
    put (show s)  >>> 
    ireturn (s > 5) 

main = print $ runIState myStateComputation 3 
-- ("3", False) 
+0

これは読んで本当の喜びでした。私たちは、Kleisliカテゴリで '' = >> :: IMonad m =>(a - > m i j b) - >(b - > m j k c) - > a - > m i k c'いくつかの点で拡張できる場合:a)これにRebindableSyntaxを使用するのは「共通」ですか? b) 'Control.Monad.Indexed'がありますが、上記のようなモナド・トランススタックでこれを使用するためのいくつかのハッキング・パッケージがありますか、自分自身をロールバックする必要がありますか? – Centril

+0

ちょうどここでスプッピングしますが、インデックス付きモナド_transformers_は、おそらくMcBrideスタイルのインデックススキームではAtkeyスタイルのインデックススキームよりもスムーズになります。マクブライドのアイデアについての良い点は、基本が理解できれば、通常のモナドのすべてが直接変換されるということです:変圧器は、 '((k - > *) - >(k - > *)) - > (k - > *) - >(k - > *)) 'であり、' type f〜> g = forall iのときは 'ilift :: ma〜> tma'となります。 f i→g i 'である。完全なストーリーのために、_Kleisli Arrows of Outrageous Fortune_を読んでください。 –

+0

'RebindableSyntax'について - このようなことは、拡張機能が設計したものとまったく同じです。実際にそれを使用するのが良いかどうかは、実際にはエンジニアリング上の決定です。コードを読む前に、それをオンにする前に、私は考えています。 –

関連する問題