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

标签 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'

看起来这个定义如我所愿:至少测试 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

as testKleisli 3 5 返回

((7, 7), 7).

重点是,人们可以分别操纵某些“计算的并行分支”中的状态,然后以某种方式将其合并。

我不熟悉箭头表示法,但它在这里很不方便:看起来它为每个计算创建了新的“分支”。是否可以使用箭头符号重写“bar”函数(来自测试的 where 子句)?

最佳答案

让我们画一张图

bar = ((put' 7) >>> get') &&& get'

让我们了解如何用箭头表示法编写它。

put' 7 and get' go along on the top line and get' goes along the bottom

就像一元 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)

哎呀,不:

ghci> test bar''' 3 5
((3,3),3)

正如您所指出的,您的状态是本地化的,并且 put' 7不会连接到 get' ,所以我们还没有设法摆脱 >>><<<组合器。

我不禁觉得这违反了阿罗定律或其他定律。嗯...

断箭定律

我花了一段时间才找到答案,但经过大量的手工脱糖和皱着眉头看图表后,我发现了一个箭头法则正盯着我,你的实例会崩溃:

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 ,如果你能遵循所有这些第一和第二!

结论:

您发现箭头表示法对于您的箭头实例不太有用,因为它假设可以通过不成立的定律进行转换。

可悲的是,虽然确实非常有趣,而且非常有趣,但它不是真正的《绿箭侠》。

关于haskell - 是否可以使用 ghc 箭头表示法重写此示例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21857635/

相关文章:

haskell - Netwire 相互依赖的电线

Haskell IO 无法让列表围绕菜单功能循环

haskell - 我将如何使用可变向量编写 imapInto ?

haskell - 你如何指定在 Cabal 文件中输出可执行文件的位置?

haskell - 通过对 Haskell 中二元函数的两个参数进行某种转换来实现 "lift up"的最惯用方法是什么?

haskell - 我可以映射一对没有箭头的第一个元素吗?

带箭头的 JavaFX 线/曲线

haskell - Haskell 守卫是如何评估的?

docker - 在 dockerfile 中安装 haskell 的 cabal 或 ghcup 不起作用

angular - 如何在 agm-map 中制作自定义箭头标记?