我重新发明了某种“状态箭头”:
请注意,这种行为有意不同于普通状态单子包装成如下箭头形式的单子:
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)
请注意,这种行为有意不同于普通状态单子包装成如下箭头形式的单子:
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).
重点是可以将状态在一些“并行计算的分支”中分别进行操作,然后以某种方式合并它。
我不熟悉箭头符号,但在这里使用它是不方便的:它看起来像是为每个计算创建新的“分支”的语法糖。是否可能使用箭头符号重写'test'的where子句中的“bar”函数?
first (StateA f) = StateA $ \s (b, d) -> let (c, s') = f s b in ((c, d), s')
(注意末尾的s'
,而不是普通的s
)吗? - Sassa NFWriter
的Monad
实例需要Monoid
,但由于这些只是函数,在两侧都有一个writer(而不是Kleisli箭头),因此基本上所有内容都在Functor
级别上完成,其中不需要Monoid
。 - leftaroundabout