哈斯克尔:我是否误解了如何使用箭头?

ram*_*ion 17 haskell arrows

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

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

所以在我经历了各种类型类定义(不确定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)
Run Code Online (Sandbox Code Playgroud)

并制定了一个使用示例

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

然而,当我尝试编译countExample,我得到"不在范围内"的错误count'count'',我想办法,我需要回到教程和什么可以用的时候读了.我认为我真正喜欢的东西更像是

countExample :: Integer
countExample =
  let (count', one) = unSF count ()
      (count'', two) = unSF count' ()
      (count''', three) = unSF count'' ()
  in three
Run Code Online (Sandbox Code Playgroud)

但这有点尴尬,我希望有点自然的东西.

任何人都可以解释我是如何误解Arrows是如何工作的,以及它们如何被使用?我错过了Arrows的基本哲学吗?

C. *_*ann 33

任何人都可以解释我是如何误解Arrows是如何工作的,以及它们如何被使用?我错过了Arrows的基本哲学吗?

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

Arrow另一方面,an的本质是函数通用版本.该Category类型类定义功能组成和恒等函数的广义版本,而Arrow类型类定义如何解除常规函数到Arrow以及如何一起工作Arrows表示多个参数(在tuples--的形式Arrows可以不一定是咖喱!).

Arrow以基本方式组合s时,就像在你的第一个countExample函数中一样,你所做的一切就像复杂的函数组合.回顾一下您的定义 - (.)您正在使用两个有状态函数并将它们连接到单个有状态函数中,并自动处理状态更改行为.

所以,你的主要问题countExample是它甚至提到了 count'这样的问题.这些都是在幕后完成的,就像你doStatemonad中使用表示法时不需要显式传递state参数一样.

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

如果您想看一个例子,该arrows软件包包含一个Arrow变换器Automaton,它定义了几乎与您相同的东西StatefulFunction,除了用任意Arrow代替您使用的普通函数.


哦,并简要回顾一下Arrows和Monads 之间的关系:

平原Arrows只是"一阶"功能的东西.正如我之前所说,它们不能总是被咖喱,同样它们也不能总是被"应用"在与($)函数应用函数相同的意义上.如果您确实需要更高阶Arrows,则类类ArrowApply定义应用程序Arrow.这为a增加了很多功能,Arrow并且允许提供相同的"崩溃嵌套结构"功能Monad,从而可以Monad为任何ArrowApply实例定义一个实例.

在另一个方向,因为Monads允许组合创建新的monadic结构的函数,对于任何Monad m你可以谈论的"Kleisli箭头",这是类型的函数a -> m b.Monad可以用Arrow非常明显的方式给出一个实例的Kleisli箭头.

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