我做什么
我发现自己写了某种“渲染器”,在某个时候我有状态转换列表:
[State String [a]]
我想一个接一个地应用它们。我可以用sequence
来做到这一点,这使我进入
State String [[a]]
然后,我不得不将结果从列表列表展平到只是列表。我可以通过映射concat
并最终获得所需的结果
State String [a]
到目前为止,很好
但是要使此工作有效,我的结果必须是list。不适用于更通用的类型,例如Traversable
,Foldable
甚至只是Monoid
。
我了解,我最终得到一个 list 结果,因为我从状态转换的 list 开始,并且序列保留了该形状。所以,如果我按照给出的那样,我的第一个问题是
State String [a]
转换为State String a
我的第二个问题是
Traversable
?。或者简而言之
答案 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)