累积[状态a]结果

时间:2019-02-09 14:11:08

标签: haskell typeclass accumulate

我做什么

我发现自己写了某种“渲染器”,在某个时候我有状态转换列表:

[State String [a]]

我想一个接一个地应用它们。我可以用sequence来做到这一点,这使我进入

State String [[a]]

然后,我不得不将结果从列表列表展平到只是列表。我可以通过映射concat并最终获得所需的结果

State String [a]

到目前为止,很好

但是要使此工作有效,我的结果必须是list。不适用于更通用的类型,例如TraversableFoldable甚至只是Monoid

我了解,我最终得到一个 list 结果,因为我从状态转换的 list 开始,并且序列保留了该形状。所以,如果我按照给出的那样,我的第一个问题是

  • 如何将State String [a]转换为State String a

我的第二个问题是

  • 这可以进一步推广吗,这样我的输入不必是列表,而可以是更通用的类型,例如Traversable?。

或者简而言之

  • 如何在累积结果的同时一个接一个地应用状态转换的集合?集合类型和结果类型的最小类型约束是什么?

2 个答案:

答案 0 :(得分:3)

GHC 8.6+提供了另一种方式,这可能更直接。它向Data.Monoid添加了一个新类型Ap,以处理在Monoid上下文中提升内部类型的Applicative实例的情况。

$ghci
GHCi, version 8.6.1: http://www.haskell.org/ghc/  :? for help
Prelude> import Data.Monoid
Prelude Data.Monoid> :t getAp . foldMap Ap
getAp . foldMap Ap
  :: (Foldable t, Applicative f, Monoid a) => t (f a) -> f a

这以极小的形式概括了[State String [a]] -> State String [a]。实际上,通过避免使用sequence,将Traversable的要求降低到Foldable。 (对Monad的{​​{1}}约束是历史工件,并且存在sequence可以将其降低到sequenceA,因此我不关心那里的区别。)这种方法与使用Applicative相比,还有一个较小的操作差异-它将单面连接和应用排序都组合为一个遍。将存在一些类型的组合,这些类型的组合效率更高,而一些类型的组合由于关联性而效率较低。这取决于sequence是否具有与mappend创建的偏差相适应的关联性偏差。

答案 1 :(得分:1)

  

如何将State String [a]转换为State String a

由于State sFunctor,因此该问题等同于询问:如何将[a]转换为a

您可以使用mconcat

为了将其应用于State,可以将fmapmconcat组合起来。

  

这可以进一步推广吗,这样我的输入不必是列表,而可以是更通用的类型,例如Traversable?。

是的,再次,核心问题是如何将任何容器t a变成a。您不需要TraversableFoldable可以。特别地,所讨论的函数是fold

再次将foldfmap结合使用以获得预期结果。