在Haskell中,为什么没有TypeClass可以像列表一样工作呢?

我正在阅读学习你一个Haskell ,我想知道为什么这么多东西都像列表一样,Prelude中没有任何东西使用types的本地工具来设置它:

“字节串的版本:被称为cons它需要一个字节和一个字节串,并且把字节放在开始位置,这很懒,所以即使字节串中的第一个块没有满,它也会产生一个新的块,这就是为什么如果你要在字节串的开头插入大量的字节,最好使用严格版本的缺点。

为什么没有TypeClass 列表或者提供了:函数来统一Data.ByteStringData.ListData.ByteString.Lazy等? 这是否有一个原因,或者这只是遗留的Haskell元素? 使用:作为一个例子有点轻描淡写,也来自LYAH:

否则,字节串模块具有类似于Data.List中的函数的负载,包括但不限于头部,尾部,init,空值,长度,映射,反转,foldl,foldr,concat,takeWhile,filter等等

ListLike包似乎提供你在找什么。 我从来没有明白为什么它不是更受欢迎。

ListLike除此之外,Prelude没有实现的一个原因是因为如果不调用某些语言扩展(多参数types的类和fundeps或相关的types),就不可能做得很好。 有三种容器需要考虑:

  1. 根本不关心它们的容器(例如[])
  2. 仅针对特定元素(如字节串)实现的容器
  3. 在元素上多态但需要上下文的容器(例如Data.Vector.Storable,它将保存任何types的可存储实例)。

这是一个非常基本的ListLike风格的类,没有使用任何扩展:

 class Listable container where head :: container a -> a instance Listable [] where head (x:xs) = x instance Listable ByteString where --compiler error, wrong kind instance Listable SV.Vector where head v = SV.head --compiler error, can't deduce context (Storable a) 

这里container有kind *->* 。 这对于字节串不起作用,因为它们不允许任意types; 他们有* 。 它也不适用于Data.Vector.Storable向量,因为该类不包含上下文(Storable约束)。

你可以通过改变你的类定义来解决这个问题

 class ListableMPTC container elem | container -> elem where 

要么

 class ListableAT container where type Elem container :: * 

现在container有种* ; 它是一个完全应用的types构造函数。 也就是说,你的实例看起来像

 instance ListableMPTC [a] a where 

但你不再是Haskell98。

这就是为什么即使是一个简单的Listabletypes的接口是不平凡的; 当你有不同的集合语义来解释(例如队列)时,它会变得更难一点。 另一个非常大的挑战是可变数据与不可变数据。 到目前为止,我所见过的每一个尝试(除了一个)都是通过创build一个可变接口和一个不可变接口来解决这个问题。 我所知道的将这两者统一起来的界面是弯曲的,引发了一系列的扩展,并且性能相当差。

附录:字节串

我完全猜测,但我认为我们坚持把字节串作为进化的产物。 也就是说,它们是低性能I / O操作的第一个解决scheme,使用Ptr Word8与IO系统调用进行接口是有意义的。 指针上的操作需要可存储,并且最有可能是必要的扩展(如上所述)使多态性工作不可用。 现在很难克服它们的势头。 具有多态性的类似容器当然是可能的,可存储的向量包实现了这一点,但是它并没有受到任何的欢迎。

字节串是否可以是多态的,对元素没有任何限制? 我认为最接近Haskell这是arraystypes。 这不如低级IO的字节串,因为数据需要从指针解压缩到数组的内部格式。 数据也是盒装的,这增加了显着的空间开销。 如果你想要无箱的存储空间(更小的空间)和高效的C接口,指针是要走的路。 一旦你有了一个Ptr,你需要Storable,然后你需要在types类中包含元素types,所以你只需要扩展。

这就是说,我认为,有了适当的扩展可用,这对于任何单个容器实现(modulo mutable / immutable API)来说基本上是一个解决的问题。 现在比较困难的部分是提出一组可用于许多不同types的结构(列表,数组,队列等)的合理类,并且足够灵活以便有用。 我个人会认为这是比较简单的,但我可能是错的。

提供了:函数来统一Data.ByteString,Data.List,Data.ByteString.Lazy等?

有人试图想出一个好的a)序列接口,b)容器接口,但是统一不同types的数据types,不同types的约束,一般会使得结果不够规范,很难想象把它们放在基础库中。 同样,对于数组,虽然Vector包中有一个相当通用的接口(基于关联的数据types)。

有几个项目用统一的接口统一这些不同的半相关数据types,所以我希望我们很快会看到一个结果。 类似的容器types。 结果不会是微不足道的。

这样一个阶级的主要问题是,即使存在,也只会提供一个肤浅的相似之处。

使用不同结构构build的相同algorithm的渐近性会有很大的不同。

在严格字节串的情况下,用cons来构build它们是非常糟糕的 ,因为每当你添加另一个string时,就会复制整个string。 这个在列表上的O(1)操作将其变成对Bytestring的O(n)操作。

当你实现可能想到的第一个algorithm时,这会导致O(n ^ 2)的行为,而构build一个列表或Data.Sequence.Seq与cons是线性时间,它可以实现在O(n)对于字节串或向量以及一点想法。

事实certificate,这样一个class级的实用性是比实际更肤浅的。

我并不是说不能find好的devise,但是这样的devise将很难使用,并且对devise的可用版本进行优化和devise可能不会被认为是Haskell 98。

我已经在我的keys包中提供了这个devise空间的一部分,它提供了很多用于索引到容器等的function,但是我故意避免提供一个类似列表的API a),因为它之前已经完成一点成功和b。)因为上面的渐近关注。

tl; dr通常,当基本操作的渐近性发生变化时,要非常不同地实现algorithm。

有两个types叫做FoldableTraversable ,它们旨在抽象列表和其他顺序数据结构的一些常见行为。 并不是所有的数据结构都有这些实例,我不知道它们是否对编译器足够透明,以至于它仍然可以对它们进行优化(是否有人知道这个问题?)

来源: 可折叠和可穿越
另请参阅此答案为什么Haskell缺less“显而易见”的types类

ByteString不是一个genericstypes。

在其他语言中,对于所有类似列表的数据结构,都有类似于Sequence的内容。 我认为这是有效的,正确的扩展名:

 class Seq ab | a -> b where head :: a -> b isTail :: a -> Bool # ([a]) is a sequence of a's instance Seq [a] a where head (x:xs) = x isTail = (== []) # ByteString is a sequence of chars instance Seq ByteString Char 

或者试试这个?

 type BS a = ByteString instance List BS 

在Haskell中为类列表数据提供types类没有太大的价值。 为什么? 因为懒惰。 你可以写一个函数把你的数据转换成列表,然后使用这个列表。 该列表将只被构build为其子列表和元素被请求,并且只要没有引用保留在前缀上,其内存就有资格收集。

对于提供一个通用的toList函数的types类是toList – 但是,它已经存在于Data.Foldable

所以基本上,解决scheme是实现Data.Foldable并使用它的toList函数。