17

我编写了一些玩具代码来玩弄箭头的概念。我想看看我是否可以编写一个箭头来编码有状态函数的概念——在不同的调用后给出不同的值。

{-# LANGUAGE Arrows#-}
module StatefulFunc where

import Control.Category
import Control.Arrow

newtype StatefulFunc a b = SF { unSF :: a -> (StatefulFunc a b, b) }

idSF :: StatefulFunc a a
idSF = SF $ \a -> (idSF, a)

dotSF :: StatefulFunc b c -> StatefulFunc a b -> StatefulFunc a c
dotSF f g = SF $ \a -> 
    let (g', b) = unSF g a
        (f', c) = unSF f b
    in (dotSF f' g', c)

instance Category StatefulFunc where
  id = idSF
  (.) = dotSF

arrSF :: (a -> b) -> StatefulFunc a b
arrSF f = ret
  where ret = SF fun
        fun a = (ret, f a)

bothSF :: StatefulFunc a b -> StatefulFunc a' b' -> StatefulFunc (a, a') (b, b')
bothSF f g = SF $ \(a,a') ->
    let (f', b) = unSF f a
        (g', b') = unSF g a'
    in (bothSF f' g', (b, b'))

splitSF :: StatefulFunc a b -> StatefulFunc a b' -> StatefulFunc a (b, b')
splitSF f g = SF $ \a ->
    let (f', b) = unSF f a
        (g', b') = unSF g a
    in (splitSF f' g', (b, b'))

instance Arrow StatefulFunc where
  arr  = arrSF
  first = flip bothSF idSF
  second = bothSF idSF
  (***) = bothSF
  (&&&) = splitSF

eitherSF :: StatefulFunc a b -> StatefulFunc a' b' -> StatefulFunc (Either a a') (Either b b')
eitherSF f g = SF $ \e -> case e of
      Left a -> let (f', b) = unSF f a in (eitherSF f' g, Left b)
      Right a' -> let (g', b') = unSF g a' in (eitherSF f g', Right b')

mergeSF :: StatefulFunc a b -> StatefulFunc a' b -> StatefulFunc (Either a a') b
mergeSF f g = SF $ \e -> case e of 
      Left a -> let (f', b) = unSF f a in (mergeSF f' g, b)
      Right a' -> let (g', b) = unSF g a' in (mergeSF f g', b)

instance ArrowChoice StatefulFunc where
  left = flip eitherSF idSF
  right = eitherSF idSF
  (+++) = eitherSF
  (|||) = mergeSF

因此,在我浏览了各种类型类定义之后(不确定 ArrowZero 是否或如何工作,所以我跳过了它),我定义了一些辅助函数

evalSF :: (StatefulFunc a b) -> a -> b
evalSF f a = snd (unSF f a)

givenState :: s -> (s -> a -> (s, b)) -> StatefulFunc a b
givenState s f = SF $ \a -> let (s', b) = f s a in (givenState s' f, b)

并制定了一个使用示例

count :: StatefulFunc a Integer
count = givenState 1 $ \c _ -> (c+1, c)

countExample :: StatefulFunc a Integer
countExample = proc _ -> do
                  (count', one) <- count -< ()
                  (count'', two) <- count' -< ()
                  (count''', three) <- count'' -< ()
                  returnA -< three

但是,当我尝试编译时,我得到andcountExample的“不在范围内”错误,我想这意味着我需要回到教程并阅读什么时候可以使用的内容。我想我真正喜欢的东西更像是count'count''

countExample :: Integer
countExample =
  let (count', one) = unSF count ()
      (count'', two) = unSF count' ()
      (count''', three) = unSF count'' ()
  in three

但这有点尴尬,我希望有一些更自然的东西。

谁能解释我如何误解箭头的工作原理以及如何使用它们?我是否缺少Arrows的基本哲学?

4

1 回答 1

33

谁能解释我如何误解箭头的工作原理以及如何使用它们?我是否缺少Arrows的基本哲学?

我的印象是你对待这个Arrow就像你对待一个Monad. 我不知道这是否算作“基本哲学”,但两者之间存在显着差异,尽管它们重叠的频率很高。从某种意义上说,定义 a 的关键Monadjoin函数;如何将嵌套结构折叠成单层。它们之所以有用是因为join它允许:您可以在递归函数中创建新的一元层,Functor根据其内容更改结构,等等。但这不是关于Monads 的,所以我们将把它留在那里。

Arrow另一方面,an 的本质是函数的一般化版本。类型类定义了函数组合和恒等函数的Category通用版本,而Arrow类型类定义了如何将常规函数提升为 anArrow以及如何使用带有Arrow多个参数的 s(以元组的形式——Arrows不一定是咖喱!)。

当以基本方式组合Arrows 时,就像在您的第一个countExample函数中一样,您真正要做的只是复杂的函数组合。回顾一下您的定义——(.)您将两个有状态函数连接成一个有状态函数,并自动处理状态更改行为。

所以,你的主要问题countExample是它甚至提到 count'了等等。这一切都是在幕后完成的,就像在monad中使用do符号时不需要显式传递状态参数一样。State

现在,因为proc符号只允许您构造大型复合Arrows,要实际使用您的有状态函数,您需要在Arrow语法之外工作,就像您需要runState或这样才能在Statemonad 中实际运行计算。你的第二个countExample是沿着这些路线,但太专业了。在一般情况下,您的有状态函数将输入流映射到输出,使其成为有限状态转换器,因此runStatefulFunction可能会采用输入值的惰性列表并使用右折叠将它们转换为输出值的惰性列表依次将每个unSF馈送到换能器。

如果你想看一个例子,这个arrows包含一个Arrow转换Automaton器,它定义了几乎与你相同的东西,除了你使用的普通函数的位置StatefulFunction是任意的。Arrow


Arrow哦,再简单回顾一下s 和s 之间的关系Monad

平原Arrows只是“一阶”功能的东西。正如我之前所说,它们不能总是被柯里化,同样它们也不能总是像($)函数应用函数一样被“应用”。如果您确实想要更高阶Arrows,则类型类ArrowApply定义一个应用程序Arrow。这增加了强大的功能Arrow,除其他外,还允许提供相同的“折叠嵌套结构”功能,从而可以为任​​何实例Monad定义一个实例。MonadArrowApply

在另一个方向,因为Monads 允许组合创建新的一元结构的函数,所以Monad m你可以谈论“Kleisli 箭头”,它是 type 的函数a -> m b。A 的 Kleisli 箭头Monad可以以Arrow非常明显的方式给出一个实例。

除了ArrowApply和 Kleisli 箭头之外,类型类之间没有特别有趣的关系。

于 2010-09-10T18:02:20.833 回答