这是一个相当有原则的解决方案,可以一次性实现头和尾(完整要点 https://gist.github.com/gallais/ef7330d74161c46a401b):
首先,我们知道如何追加列表(稍后会很有用):
append :: List a -> List a -> List a
append (Abstr xs) (Abstr ys) = Abstr (\ f -> xs f <> ys f)
然后我们引入一个新类型Split
我们将用它来检测是否List
是否为空(如果非空,则获取头部和尾部):
newtype Split a = Split { outSplit :: Maybe (a, List a) }
这种新类型形成了一个幺半群:我们确实知道如何附加两个列表。
instance Monoid (Split a) where
mempty = Split Nothing
mappend (Split Nothing) (Split nns) = Split nns
mappend (Split mms) (Split Nothing) = Split mms
mappend (Split (Just (m, ms))) (Split (Just (n, ns))) =
Split $ Just (m, append ms (cons n ns))
这意味着我们可以得到一个函数List a
to Split a
using List a
's apply
:
split :: List a -> Split a
split xs = apply xs $ \ a -> Split $ Just (a, nil)
head
and tail
最终可以简单地导出split
:
head :: List a -> Maybe a
head = fmap fst . outSplit . split
tail :: List a -> Maybe (List a)
tail = fmap snd . outSplit . split