Haskell 真正无序的折叠袋

Haskell 真正无序的折叠袋,haskell,types,bag,commutativity,Haskell,Types,Bag,Commutativity,我想要一个袋子容器,它可以对客户隐藏“真实”的订单 它还必须是完全多态的,即不需要对其元素类型进行任何约束 我发现至少有三种Bag实现:Bagmodulefromghcpackage,Data.BagfromBag和Math.combinations.MultisetfromMultiset comb 但是,它们都具有toList和fold*操作,这些操作公开了元素的内部顺序,这可能取决于实现细节或包构造的顺序 toList是不可能的,至少对于类型行李a->[a]。但是,折叠并不总是公开顺序 例

我想要一个袋子容器,它可以对客户隐藏“真实”的订单

它还必须是完全多态的,即不需要对其元素类型进行任何约束

我发现至少有三种Bag实现:
Bag
modulefrom
ghc
package,
Data.Bag
from
Bag
Math.combinations.Multiset
from
Multiset comb

但是,它们都具有
toList
fold*
操作,这些操作公开了元素的内部顺序,这可能取决于实现细节或包构造的顺序

toList
是不可能的,至少对于类型
行李a->[a]
。但是,折叠并不总是公开顺序

例如,
fold(+)0
不会公开

问题是,我应该如何设计折叠界面?
a->a->a
折叠功能是否有安全的充分必要条件?由于
fmap
没有公开顺序,使用
a->b->b
折叠是否会丢失泛型


我在考虑交换幺半群-它们似乎足够了,但我不确定是否需要结合性和标识元素。

如果你的包可以是空的,那么标识可能是必要的-在这种情况下,你必须返回一些东西,如果你想让折叠成为同态的话(因此,组合折叠某些袋子的结果与组合袋子制成的袋子的折叠结果相同,这是一个非常自然的特性),它必须是一个标识元素

关联性也是一个好主意。假设我有这样一个类型和操作:

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
来区分
包[1,1,2,1,3]
,它将折叠成
2(1)(2(1)(1 3))
包[1,1,2,1]
,它将折叠成
2(1,3)(2)(1,2))
(注意,并不是所有的结构都被保留,但是在一个长的列表中,除了最后两个元素的顺序之外,我将恢复整个列表的顺序)


从本质上讲,如果你将所有的项与一个操作结合在一起,你就会有一个应用程序树,类似于
A++(b++(c+++d))
。交换性可以让你进行一些重新排列,但无论你做什么,
c
总是与
d
结合在一起。因此,如果你想让它与
(A+++c)+-+(b+-+d)
,您也确实需要关联性。

您可以用foldl或foldr实现toList,并使用类型签名a->b->b(即a->[a]->[a],)这确实暴露了固有的顺序。但是,我不太清楚为什么你真的不想这么做。即使是像a->a->a这样的折叠,你也无法避免有人用unsafePerformIO暴露内部顺序。我在想也许
reduce::Monoid m=>(a->m)->Bag a->m
会起作用,其思想是
reduce
的调用者唯一能看到的是独立的
Bag
元素。问题是调用者还可以实现
Monoid
实例,从而能够观察到元素的一些具体顺序。我担心这很重要可能有一个枚举元素的操作,但禁止客户端将自己耦合到它生成元素的或有顺序。唯一的解决方案可能是以定义良好的顺序生成元素。使用这些扩展注释的答案。
交换幺半群m=>(a->m)->Bag a->m
是一个好主意,因为它的类型向调用者传递了足够的信息,以避免行为不端的函数,并设计了一个定义良好的函数库。如果调用者想要伤害自己,我不想阻止他们伤害自己,我只想避免意外的错误。例如,如果我只使用股票交换幺半群来减少我的代码不依赖于顺序,这是一个非常有力的保证。我问这个问题是因为我遇到了一个列表monad,它不是一个完全的monad,因为它通过排列违反了法律。如果不添加约束,一个定义良好的顺序是不可能实现的。但是值得一提的是,您必须信任您的用户来建立这些预设置假设-实际上没有任何方法可以静态地强制执行它们。我知道如果不给用户带来巨大的证明负担,这是不可能的。我只需要一些方法来告诉用户预期的不变量。使用
class Monoid m=>commissivemonoid m
而不使用“方法”似乎是合适的-用户可以声明他们的Monoid comm如果他们确信他们是,那么他们是可以改变的。