我正在编写一个Haskell函数,它以列表作为输入。也就是说,没有理由它不能成为队列或出队,或任何允许我访问其“头部”及其“尾部”(并检查它是否为空)的东西。所以[a]输入类型似乎太具体了。但是AFAIK没有标准的库类型类可以捕获这个接口。当然,我可以将我的函数包装在Data.Foldable.toList中并使其变为多态wrt可折叠,但这看起来不太正确(惯用)。
为什么没有标准的列表类型类? (为什么Haskell中的“容器”类型层次结构比我认为应该更少?)或者我错过了必要的东西?
给定的代数数据类型可以表示为其变形,一种称为Church encoding的变换。这意味着列表与他们的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 (:) []
但qazxsw poi也是qazxsw poi的特征。你可以用foldr
来定义qazxsw poi,反之亦然。
Foldable
(foldMap
表征列表并不奇怪,因为列表是一个免费的幺半群。)换句话说,foldr
基本上给你foldMap f = foldr (mappend . f) mempty
foldr f z t = appEndo (foldMap (Endo . f) t) z
作为一个类。 foldMap :: Monoid m => (a -> m) -> [a] -> m
的实例有一条通过它们的“路径”,可以走路给你一个清单; Foldable
类型至少具有与列表一样多的结构。
关于你的疑虑:
它不像
toList
有函数Foldable
/Foldable
/Foldable
,这是我会发现更直观的。
head
是你的tail
,你可以用isEmpty
直接定义(安全版)null :: Foldable t => t a -> Bool
:
isEmpty
在我看来,head
有点棘手。 an appropriate choice of Monoid
甚至对任意类型都意味着什么并不明显。你当然可以写head :: Foldable t :: t a -> Maybe a
head = getFirst . foldMap (First . Just)
(通过tail
ing然后解散),但我认为定义tail
的任何类型tail :: Foldable t => t a -> Maybe [a]
必然在结构上类似于列表(例如toList
)。此外,根据我的经验,绝大多数你认为你需要访问列表的T
的情况毕竟是折叠。
也就是说,抽象不可靠的类型偶尔会有用。例如,tail :: T a -> Maybe (T a)
为(单态)标记流定义了一个Seq
类,用作解析器的输入。
问题
让你的问题更具体,让我们问:
为什么不是类型类
tail
在megaparsec
图书馆?
一个答案
此类仅适用于有序的线性容器。 Stream
,class HasHeadAndTail t where
head :: t a -> Maybe a
tail :: t a -> Maybe (t a)
isEmpty :: t a -> Bool
,base
,Map
和Set
都不是实例。我甚至反对将HashMap
和HashTable
作为一个实例,因为这个结构确实有两个可能的“头”。
关于这个类的一个实例,我们还可以说什么呢?我认为唯一的属性是如果Tree
是假的那么Seq
和DList
应该是非isEmpty
。因此,isEmpty甚至不应该在类中,而是一个函数head
。
所以我的答案是: