我试图通过编写一些我需要的东西来理解单子和箭头把我的头绕过去:
newtype Stateful a b = Stateful { runStateful :: a -> (b, Stateful a b) }
stateful :: (a -> (b, Stateful a b)) -> Stateful a b
stateful = Stateful
-- executes a stateful computation, returning its result
evalStateful :: Stateful a b -> a -> b
evalStateful f x = fst $ runStateful f x
-- execStateful :: ??
execStateful = undefined
-- converts a stateful function to Stateful
close :: (s -> a -> (b, s)) -> s -> Stateful a b
close f s = stateful $ \x -> let (y, s') = f s x
in (y, close f s')
-- function composition
(<.>) :: Stateful a b -> Stateful b c -> Stateful a c
f <.> g = stateful $ \x -> let (y, f') = runStateful f x
(z, g') = runStateful g y
in (z, f' <.> g')
-- lifts a stateful computation to perform on lists
maps :: Stateful a b -> [a] -> [b]
maps f [] = []
maps f (x:xs) = let (x',f') = runStateful f x
in x' : maps f' xs
所以想法是你有一个函数,它接受并返回一个类似的状态(a -> s -> (b, s))
,但是我们修改它,而不是返回一个输出/状态元组,我们返回一个输出/X 元组,其中 X 与我们的初始函数的类型相同。所以它有点像(a -> (b, a -> (b, ...)))
。我最初试图做一些类似于 State monad 的东西,但不是返回 state,而是返回与它本身相同的函数,状态部分应用(因此状态和要应用的函数保持耦合在一起)。
所以我想也许这只是 State monad 所做的事情,但我想要的用例之一是能够将多个有状态函数链接在一起,每个函数都有自己的状态,独立于其他函数。我还没有想出一种方法来以一种优雅的方式将多个独立的类型函数链接在一起(s -> a -> (b, s))
,(s' -> b -> (c, s'))
这不会导致返回的状态是一些丑陋的元组序列,就像(s''', (s'', (s', s)))
你做的更多的链一样,所以这让我觉得也许这比状态单子更复杂。
我还阅读了关于用电路隐喻描述的箭头以及用于流处理的箭头,这符合我的设想。我看到有一个 ArrowLoop,但是当我花了一段时间试图弄清楚它似乎更多地与巧妙地使用惰性而不是处理更新状态有关。然后我还读到 IO monad 使用了一些类型系统的诡计来不可避免,这感觉就像我用 Stateful 完成的一样;一方面, Stateful 的类型不编码其内部状态的类型,因此很难(不可能?)推断execStateful
. 另一方面,我想不出任何有意义的绑定运算符。
所以我不太确定我在做什么。我认为它至少是一个函子,因为 fmap 只是在电路末端组成一个任意函数是有道理的,确保内部状态管理保持不变。我几乎确信自己 bind 可以用同样的方式定义,但每当我试图把这个想法具体化时,它就会崩溃。任何人都可以阐明我正在使用的东西吗?这是一些一般的单子还是箭头?一个特定的单子或箭头?
此外,使用这种方法是否有任何明显的问题或改进?我希望内部状态能够使用 ST monad,因为我设想内部状态由昂贵的就地操作组成。但我并不真正了解各种抽象来了解我的设计是否可扩展。
编辑:我需要做的一件事是确保不能引用原始的 Stateful 函数,因为它会引用旧状态而不是新更新的状态,我不知道该怎么做。本质上,runStateful 只能在有状态数据类型上使用一次,然后您需要确保下次运行它时使用返回的Stateful a b
而不是原始的。内部管道可以很容易地防止意外发生,但我不知道如何使它成为不可能。