我重新发明了某种“状态箭头”:
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'
似乎这个定义按我的意愿运作:至少 test 3 5 产生
((7,3), 3)
请注意,这种行为有意与普通的状态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'函数(来自test的where子句)?
答案 0 :(得分:11)
让我们画一幅
的照片bar = ((put' 7) >>> get') &&& get'
让我们了解如何用箭头表示法编写它。
与monadic do
表示法一样,proc
表示法引入了命名变量,将>>=
等组合符替换为显式传递值。
无论如何,我们可以看到我们需要将输入x
提供给双方,给出:
bar' = proc x -> do
wasput <- put' 7 >>> get' -< x
justgot <- get' -< x
returnA -< (wasput,justgot)
或者如果我们希望一切从右到左,等效
bar'' = proc x -> do
wasput <- get' <<< put' 7 -< x
justgot <- get' -< x
returnA -< (wasput,justgot)
我重构test
以进行多项测试:
test s b = (flip runStateA) s b
所以我们得到
ghci> test bar 3 5
((7,3),3)
ghci> test bar' 3 5
((7,3),3)
ghci> test bar'' 3 5
((7,3),3)
>>>
的情况下编写吗?我们可能想要考虑(>>>)
:
bar''' = proc x -> do
put7 <- put' 7 -< x
wasput <- get' -< put7
justgot <- get' -< x
returnA -< (wasput,justgot)
oops,no:
ghci> test bar''' 3 5
((3,3),3)
正如您所指出的那样,您的状态已经过本地化,而put' 7
并未通过get'
,因此我们无法摆脱>>>
1}}或<<<
组合。
我无法感觉到这违反了一些箭法或其他法律。嗯...
我花了一段时间才追查,但经过大量的手工贬低和皱着眉头的图表后,我发现了一条箭法让我盯着你的实例打破了:
first (f >>> g) = first f >>> first g
如果我们定义
dup :: Arrow a => a t (t, t)
dup = arr (\x -> (x,x))
我们得到了
ghci> test (dup >>> (first (put' 7 >>> get'))) 1 3
((7,3),1)
ghci> test (dup >>> (first (put' 7) >>> first get')) 1 3
((1,3),1)
这是因为第二个示例中的put' 7
中的本地化状态并不会成为第二个first
,如果您可以遵循所有这些第一和第二个!
您发现箭头符号对箭头实例的用处不大,因为它假设可以通过不守法的法则进行转换。
可悲的是,确实非常有趣,并且极度转移,它不是真正的箭头。