给定的代数数据类型可以表示为其变形,这种变换称为教堂编码 https://en.wikipedia.org/wiki/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 (:) []
But foldr
还表征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 https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Foldable.html#v:null是你的isEmpty
,并且您可以定义(安全版本)head
直接与适当的选择Monoid https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Monoid.html#t:First:
head :: Foldable t :: t a -> Maybe a
head = getFirst . foldMap (First . Just)
tail
在我看来有点棘手。不清楚是什么tail
甚至意味着任意类型。你当然可以写tail :: Foldable t => t a -> Maybe [a]
(by toList
ing 然后 unconsing),但我认为任何类型T
为此tail :: T a -> Maybe (T a)
定义在结构上必然类似于列表(例如Seq https://hackage.haskell.org/package/containers-0.5.10.1/docs/Data-Sequence.html#t:Seq)。此外,根据我的经验,在绝大多数情况下,您认为您需要访问列表的tail
原来是折叠。
也就是说,对不可用的类型进行抽象有时是有用的。megaparsec https://hackage.haskell.org/package/megaparsec-5.3.0,例如,定义一个Stream https://hackage.haskell.org/package/megaparsec-5.3.0/docs/Text-Megaparsec.html#t:Stream用作解析器输入的(单态)标记流的类。