我写了一些玩具代码来玩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
所以在我完成各种类型类定义后(不确定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
但是,当我尝试编译countExample
时,count'
和count''
会出现“不在范围内”的错误,我想这意味着我需要回到教程并阅读什么时候可以使用。我认为我真正喜欢的东西更像是
countExample :: Integer
countExample =
let (count', one) = unSF count ()
(count'', two) = unSF count' ()
(count''', three) = unSF count'' ()
in three
但这有点尴尬,我希望有点自然。
任何人都可以解释我是如何误解Arrows是如何工作的,以及它们如何被使用?我错过了Arrows的基本哲学吗?
答案 0 :(得分:33)
任何人都可以解释我是如何误解Arrows是如何工作的,以及它们如何被使用?我错过了Arrows的基本哲学吗?
我得到的印象是,您要像对待Arrow
一样对待这个Monad
。我不知道这是否算作“基本哲学”,但两者之间存在显着差异,尽管它们经常重叠。从某种意义上说,定义Monad
的关键是join
函数;如何将嵌套结构折叠为单个图层。由于join
允许,它们很有用:您可以在递归函数中创建新的monadic图层,根据其内容更改Functor
结构,依此类推。但这不是关于Monad
s,所以我们将其留在那里。
另一方面,Arrow
的本质是广义的函数版本。 Category
类型类定义了函数组合和标识函数的通用版本,而Arrow
类型类定义了如何将常规函数提升为Arrow
以及如何使用{{1}带有多个参数的s(以元组的形式 - Arrow
不一定是咖喱!)。
以基本方式组合Arrows
时,就像在您的第一个Arrow
函数中一样,您所做的一切就像精心设计的功能组合。回顾一下countExample
的定义 - 你正在使用两个有状态函数并将它们连接成一个有状态函数,状态更改行为会自动处理。
因此,(.)
的主要问题是它甚至提及 countExample
等。这些都是在幕后完成的,就像在count'
monad中使用do
表示法时不需要显式传递状态参数一样。
现在,因为State
表示法只允许您构建大型复合proc
,实际使用您的有状态函数,您需要在{{1}之外工作语法,就像你需要Arrow
之类的,以便在Arrow
monad中实际运行计算。你的第二个runState
沿着这些方向,但太专业了。在一般情况下,您的有状态函数将输入的流映射到输出的流,使其成为finite state transducer,因此可能State
获取一个惰性的输入值列表,并使用countExample
的右侧折叠将它们转换为输入值的惰性列表,以便依次将每个输入值提供给传感器。
如果您想查看示例,runStatefulFunction
包includes an Arrow
transformer Automaton
定义的内容几乎与您的unSF
完全相同,除非有arrows
代替StatefulFunction
您使用的普通功能。
哦,并简要回顾一下Arrow
和Arrow
之间的关系:
普通Monad
只是“一阶”函数式的东西。正如我之前所说,它们不能总是被咖喱,同样它们也不能总是被“应用”,就像Arrows
函数应用函数一样。如果您确实需要更高阶($)
,则类型类Arrows
会定义应用程序ArrowApply
。这为Arrow
增加了很多的功能,除此之外,还允许Arrow
提供相同的“崩溃嵌套结构”功能,从而可以定义任何Monad
实例的Monad
实例。
在另一个方向,因为ArrowApply
允许组合创建新monadic结构的函数,对于任何Monad
Monad
,你可以谈论一个“Kleisli箭头”,这是一个函数类型为m
。 a -> m b
的Kleisli箭头可以以非常明显的方式给出Monad
实例。
除了Arrow
和Kleisli箭头之外,类型类之间没有特别有趣的关系。