我想要一个袋子容器,它隐藏了客户的“真实”订单。
它也必须是完全多态的,不应该对其元素类型有任何约束。
我找到了至少三个行李的实现:来自Bag
包的ghc
模块,来自Data.Bag
的{{1}}和来自bag
的{{1}}。
但是,它们都有Math.Combinatorics.Multiset
和multiset-comb
个操作,这些操作会暴露元素的内部顺序,这可能取决于实现细节或包构造的顺序。
toList
是不可能的,至少是fold*
类型。但是,折叠并不总是暴露订单。
例如,toList
不会公开。
问题是,我该如何设计折叠界面? Bag a -> [a]
折叠功能的安全是否有必要且充分的条件?由于fold (+) 0
没有公开订单,因此使用a -> a -> a
折叠是否会失去通用性?
我正在考虑可交换的幺半群 - 它们似乎已经足够了,但我不确定是否有必要使用关联性和身份元素。
答案 0 :(得分:6)
如果您的行李可能是空的,则可能需要身份证明 - 在这种情况下您必须返回某些内容,并且如果您希望您的折叠是同态的(这样折叠某些行李的结果与折叠行李相同)通过组合袋子制成的袋子,这是一种非常自然的特性,它必须是一个标识元素。
同样,相关性是一个好主意。假设我有类似的操作:data T a = Zero | One a | Two (T a) (T a)
deriving (Eq, Ord, Show)
(+-+) :: Ord a => T a -> T a -> T a
Zero +-+ x = x
x +-+ Zero = x
a +-+ b = Two (min a b) (max a b)
显然(+-+)
是可交换的并且具有同一性,但是不具有关联性。假设我然后将一个包作为列表实现:
newtype Bag a = Bag [a]
-- pre-condition: f is commutative and z is an identity for it
foldBag :: (a -> a -> a) -> a -> Bag a -> a
foldBag f z (Bag xs) = foldr f z xs
foldNonAssoc :: (Ord a) => Bag (T a) -> T a
foldNonAssoc = foldBag (+-+) Zero
即使我要求所述的前提条件,我仍然可以使用我的foldNonAssoc
来区分Bag [One 1,One 2,One 3]
,它将折叠为Two (One 1) (Two (One 2) (One 3))
和Bag [One 3,One 2,One 1]
,这将折叠为Two (One 3) (Two (One 1) (One 2))
(请注意,不会保留所有结构,但在长列表中,除了最后两个元素的排序之外,我将获得整个列表顺序。)
先验地,如果您将所有项目与操作组合在一起,您将拥有一个应用程序树,例如a +-+ (b +-+ (c +-+ d))
。交换性可以让你做一些重新安排,但不管你做什么,c
总是与d
结合。因此,如果您希望它与(a +-+ c) +-+ (b +-+ d)
相同,那么您也需要关联性。