[英]Why is Haskell [] (list) not a type class?
I am writing a Haskell function which takes a list as input. 我正在编写一个Haskell函数,它以列表作为输入。 That is, there's no reason it couldn't be a queue or dequeue, or anything that allows me to access its "head" and its "tail" (and check if it's empty).
也就是说,没有理由它不能成为队列或出队,或任何允许我访问其“头部”及其“尾部”(并检查它是否为空)的东西。 So the [a] input type seems too specific.
所以[a]输入类型似乎太具体了。 But AFAIK there's no standard library typeclass that captures exactly this interface.
但是AFAIK没有标准的库类型类可以捕获这个接口。 Sure, I could wrap my function in a Data.Foldable.toList and make it polymorphic wrt Foldable, but that doesn't quite seem right (idiomatic).
当然,我可以将我的函数包装在Data.Foldable.toList中并使其变为多态wrt可折叠,但这看起来不太正确(惯用)。
Why is there no standard list type class? 为什么没有标准的列表类型类? (And why is the "container" type class hierarchy in Haskell less developed than I think it should be?) Or am I missing something essential?
(为什么Haskell中的“容器”类型层次结构比我认为应该更少?)或者我错过了必要的东西?
A given algebraic datatype can be represented as its catamorphism, a transformation known as Church encoding . 给定的代数数据类型可以表示为其变形,一种称为教会编码的变换。 That means lists are isomorphic to their
foldr
: 这意味着列表与其
foldr
同构:
type List a = forall b. (a -> b -> b) -> b -> b
fromList :: [a] -> List a
fromList xs = \f z -> foldr f z xs
toList :: List a -> [a]
toList l = l (:) []
But foldr
also characterises Foldable
. 但
foldr
也刻画Foldable
。 You can define foldMap
in terms of foldr
, and vice versa. 您可以根据
foldr
定义foldMap
,反之亦然。
foldMap f = foldr (mappend . f) mempty
foldr f z t = appEndo (foldMap (Endo . f) t) z
(It shouldn't be surprising that foldMap :: Monoid m => (a -> m) -> [a] -> m
characterises lists, because lists are a free monoid.) In other words, Foldable
basically gives you toList
as a class. (
foldMap :: Monoid m => (a -> m) -> [a] -> m
表示列表是不足为奇的,因为列表是一个免费的幺半群。)换句话说, Foldable
基本上给你toList
as一类。 Instances of Foldable
have a "path" through them which can be walked to give you a list; Foldable
实例有一条通过它们的“路径”,可以走路给你一个列表; Foldable
types have at least as much structure as lists. Foldable
类型至少具有与列表一样多的结构。
Regarding your misgivings: 关于你的疑虑:
It's not like
Foldable
has functionshead
/tail
/isEmpty
, which is what I would find more intuitive.它不像
Foldable
具有head
/tail
/isEmpty
函数,这是我会发现更直观的。
null :: Foldable t => ta -> Bool
is your isEmpty
, and you can define (a safe version of) head
straightforwardly with an appropriate choice of Monoid
: null :: Foldable t => ta -> Bool
是你的isEmpty
,你可以通过适当选择Monoid
直接定义(一个安全版本) head
:
head :: Foldable t :: t a -> Maybe a
head = getFirst . foldMap (First . Just)
tail
is kinda tricky in my opinion. 在我看来,
tail
有点棘手。 It's not obvious what tail
would even mean for an arbitrary type. 对于任意类型,
tail
甚至意味着什么并不明显。 You can certainly write tail :: Foldable t => ta -> Maybe [a]
(by toList
ing and then unconsing), but I think any type T
for which tail :: T a -> Maybe (T a)
is defined would necessarily be structurally similar to lists (eg Seq
). 你当然可以编写
tail :: Foldable t => ta -> Maybe [a]
(通过toList
ing然后解散),但我认为任何类型T
的tail :: T a -> Maybe (T a)
都是定义的必须在结构上类似于列表(例如Seq
)。 Besides, in my experience, the vast majority of cases where you'd think you need access to a list's tail
turn out to be folds after all. 此外,根据我的经验,绝大多数你认为你需要访问列表
tail
毕竟是折叠的。
That said, abstracting over unconsable types is occasionally useful. 也就是说,抽象不可靠的类型偶尔会有用。
megaparsec
, for example, defines a Stream
class for (monomorphic) streams of tokens to be used as input for a parser. 例如,
megaparsec
为(单态)标记流定义了一个Stream
类,用作解析器的输入。
The Question 问题
Making your question more concrete, let's ask: 让你的问题更具体,让我们问:
Why isn't the type class 为什么不是类型类
class HasHeadAndTail t where
head :: t a -> Maybe a
tail :: t a -> Maybe (t a)
isEmpty :: t a -> Bool
in the base
library? 在
base
库?
An Answer 答案
This class is only useful for ordered, linear containers. 此类仅适用于有序的线性容器。
Map
, Set
, HashMap
, HashTable
, and Tree
all would not be instances. Map
, Set
, HashMap
, HashTable
和Tree
都不是实例。 I'd even argue against making Seq
and DList
an instance since there are really two possible "heads" of that structure. 我甚至反对将
Seq
和DList
作为一个实例,因为该结构实际上有两个可能的“头”。
Also what can we say about any type that is an instance of this class? 关于这个类的一个实例,我们还可以说什么呢? I think the only property is if
isEmpty
is False then head
and tail
should be non- Nothing
. 我认为唯一的属性是如果
isEmpty
是False那么head
和tail
应该是非Nothing
。 As a result, isEmpty shouldn't even be in the class and instead be a function isEmpty :: HashHeadAndTail t => ta -> Bool ; isEmpty = isNothing . head
因此,isEmpty甚至不应该在类中,而是一个函数
isEmpty :: HashHeadAndTail t => ta -> Bool ; isEmpty = isNothing . head
isEmpty :: HashHeadAndTail t => ta -> Bool ; isEmpty = isNothing . head
isEmpty :: HashHeadAndTail t => ta -> Bool ; isEmpty = isNothing . head
. isEmpty :: HashHeadAndTail t => ta -> Bool ; isEmpty = isNothing . head
。
So my answer is: 所以我的答案是:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.