我重新发明了某种“状态箭头”:
import Prelude hiding (id, (.))
import Control.Monad.State
import Control.Arrow
import Control.Category
data StateA s a b = StateA {runStateA :: s -> a -> (b, s)}
instance Category (StateA s) where
id = StateA (\s a -> (a, s))
(StateA f) . (StateA g) = StateA $ \s x -> let (b, s') = g s x in f s' b
instance Arrow (StateA s) where
arr f = StateA $ \s a -> (f a, s)
first (StateA f) = StateA $ \s (b, d) -> let (c, s') = f s b in ((c, d), s)
put' :: s -> StateA s b ()
put' s = StateA $ \_ _ -> ((), s)
get' :: StateA s b s
get' = StateA $ \s _ -> (s, s)
merge :: (s -> s -> s) -> StateA s a b -> StateA s a c -> StateA s a (b, c)
merge f (StateA a) (StateA b) = StateA $ \s x ->
let (ra, sa) = a s x
(rb, sb) = b s x
in ((ra, rb), f sa sb)
test = (flip runStateA) s bar
where bar = ((put' 7) >>> get') &&& get'
似乎这个定义如我所愿:至少测试 3 5产量
((7,3), 3)
请注意,这种行为故意不同于像这样包裹在箭头中的普通 State monad:
liftKC = Kleisli . const
putM :: a -> Kleisli (State a) b ()
putM = liftKC . put
getM :: Kleisli (State a) b a
getM = liftKC get
foo :: (Num a) => Kleisli (State a) a (a, a)
foo = (putM 7 >>> getM) &&& getM
testKleisli a b = (flip runState) a $
(flip runKleisli) b foo
作为testKleisli 3 5返回
((7, 7), 7).
关键是人们可以分别操纵某些“并行计算分支”中的状态,然后以某种方式合并它。
我不熟悉箭头符号,但在这里很不方便:看起来它为每次计算都创建了新的“分支”。是否可以使用箭头符号重写“bar”函数(来自测试的 where 子句)?