是否可以使用ghc箭头表示法重写此示例?

use*_*391 4 haskell arrows

我重新发明了某种"状态箭头":

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'
Run Code Online (Sandbox Code Playgroud)

似乎这个定义按我的意愿运作:至少测试3 5收益率

((7,3), 3)
Run Code Online (Sandbox Code Playgroud)

注意,这种行为故意不像普通的状态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
Run Code Online (Sandbox Code Playgroud)

因为testKleisli 3 5返回

((7, 7), 7).
Run Code Online (Sandbox Code Playgroud)

关键是人们可以分别在一些"并行计算分支"中操纵状态,然后以某种方式合并它.

我不熟悉箭头符号,但这里很不方便:它看起来像是为每次计算创建新的"分支".是否可以使用箭头符号重写'bar'函数(来自test的where子句)?

And*_*ewC 11

我们来画一幅画

bar = ((put' 7) >>> get') &&& get'
Run Code Online (Sandbox Code Playgroud)

给我们一个如何用箭头符号写它的想法.

把'7并得到'放在最上面然后'沿着底部走

与monadic do表示法一样,proc符号引入命名变量,替换组合符,例如>>=显式传递值.

无论如何,我们可以看到我们需要向x双方提供输入,给出:

bar' = proc x -> do
        wasput <- put' 7 >>> get' -< x
        justgot <- get' -< x
        returnA -< (wasput,justgot)
Run Code Online (Sandbox Code Playgroud)

或者,如果我们希望一切从右到左,等效

bar'' = proc x -> do
        wasput <- get' <<< put' 7 -< x
        justgot <- get' -< x
        returnA -< (wasput,justgot)
Run Code Online (Sandbox Code Playgroud)

测试

我将重构test多个测试:

test s b = (flip runStateA) s b
Run Code Online (Sandbox Code Playgroud)

所以我们得到了

ghci> test bar 3 5
((7,3),3)
ghci> test bar' 3 5
((7,3),3)
ghci> test bar'' 3 5
((7,3),3)
Run Code Online (Sandbox Code Playgroud)

我们可以不写>>>吗?

我们可能想要分解(>>>):

bar''' = proc x -> do
        put7 <- put' 7 -< x
        wasput <- get' -< put7
        justgot <- get' -< x
        returnA -< (wasput,justgot)
Run Code Online (Sandbox Code Playgroud)

哎呀,没有:

ghci> test bar''' 3 5
((3,3),3)
Run Code Online (Sandbox Code Playgroud)

正如你所指出的那样,你的状态是本地化的,并且put' 7它没有穿过任何一个get',所以我们没有设法摆脱>>><<<组合.

我不禁觉得这违反了一些箭法或其他法律.嗯...

断箭法

我花了一段时间才追查,但是经过大量的手工贬低和皱眉图表后,我发现了一个箭头法,盯着我的脸,你的实例破了:

first (f >>> g) = first f >>> first g
Run Code Online (Sandbox Code Playgroud)

如果我们定义

dup :: Arrow a => a t (t, t)
dup = arr (\x -> (x,x))    
Run Code Online (Sandbox Code Playgroud)

我们得到

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)
Run Code Online (Sandbox Code Playgroud)

这是因为如果您可以遵循所有这些第一和put' 7第二first,那么第二个示例中的本地化状态不会进入第二个!

结论:

您发现箭头符号对箭头实例的用处不大,因为它假设可以通过不能保留的法则进行转换.

可悲的是,确实非常有趣,并且异常转移,这不是一个真正的箭.