ListLike包似乎提供了您需要的内容。我从未理解为什么它不太受欢迎。
除了ListLike之外,这个函数没有被实现到Prelude中的原因是,如果不调用一些语言扩展(多参数类型类和fundeps或关联类型),就无法很好地完成它。有三种容器需要考虑:
1. 完全不关心其元素的容器(例如[])。
2. 仅针对特定元素实现的容器(例如bytestrings)。
3. 对元素具有多态性但需要上下文的容器(例如Data.Vector.Storable,它将保持任何具有可存储实例的类型)。
以下是一个非常基本的ListLike风格的类,而不使用任何扩展:
class Listable container where
head :: container a -> a
instance Listable [] where
head (x:xs) = x
instance Listable ByteString where
instance Listable SV.Vector where
head v = SV.head
这里的
container
类型为
*->*
,但对于字节数组并不适用,因为它们不允许任意类型;它们的类型为
*
。此外,对于Data.Vector.Storable向量也不适用,因为该类不包括上下文(即Storable限制)。
你可以通过更改类定义来解决这个问题:
class ListableMPTC container elem | container -> elem where
或者
class ListableAT container where
type Elem container :: *
现在
container
的类型为
*
,它是一个完全应用的类型构造函数。也就是说,您的实例看起来像这样:
instance ListableMPTC [a] a where
但您不再是Haskell98。
这就是为什么即使是简单的可列表类型接口也是非平凡的原因;当您需要考虑不同的集合语义(例如队列)时,它会变得更加困难。另一个真正大的挑战是可变与不可变数据。到目前为止,我见过的每一次尝试(除了一次)都通过创建一个可变接口和一个不可变接口来解决该问题。我知道的唯一统一两者的接口是令人费解的,调用了一堆扩展,并且性能相当差。
附录:字节串
我完全是在猜测,但我认为我们被演化的产物所束缚,必须使用字节串来解决低性能I/O操作的问题,而使用Ptr Word8
与IO系统调用进行交互是有意义的。指针操作需要Storable,并且最可能使多态性起作用所需的扩展(如上所述)当时还不可用。现在很难克服它们的动力。类似的容器具有多态性肯定是可能的,storablevector包实现了这一点,但它远远不如bytestring流行。
字节串是否可以没有任何元素限制而是多态的?我认为Haskell最接近这个问题的解决方案是Array类型。与bytestring相比,这不太适合低级别的I/O,因为需要将数据从指针解包到数组的内部格式中。此外,数据是装箱的,这会增加显着的空间开销。如果您想要未装箱的存储(更少的空间)和与C的高效交互,则应使用指针。一旦您拥有了一个Ptr,您就需要Storable,然后您需要在类型类中包含元素类型,因此您只剩下扩展要求。
话虽如此,我认为对于任何单个容器实现来说,只要有适当的扩展可用,这基本上是一个已解决的问题(除了可变/不可变API)。现在更难的部分是提出一组合理的类,可用于许多不同类型的结构(列表、数组、队列等),并且足够灵活以便有用。我个人认为这应该相对简单,但我可能错了。
ByteString
又有[]
作为实例,因为[]
具有* -> *
的种类,而ByteString
只是*
。 - Travis Brown=
的类型类,并且预定义了每种数值类型的不同函数来测试相等性;此外,每个库都遵循相同的约定。那么,难道不应该在“语言本身”中使用类型类吗?如果你能回答这个问题,你可能已经准备好回答我的问题了。 - Evan Carroll