We didn't rule out future generalizations across base, but tried to limit our scope to things that had already been generalized for this first round. (After a libraries proposal in October or so that shifted a bit, with length
and null
getting generalized.)
That said, most of the methods you mention involve "changing a shape"
We could generalize some of those methods, sure:
catMaybes :: Foldable f => f (Maybe a) -> [a]
mapMaybes :: Foldable f => (a -> Maybe b) -> f a -> [b]
listToMaybe :: Foldable f => f a -> Maybe a
but many of the names wind up being rather incoherent for their broader purpose. e.g. listToMaybe
is really more of a safeHead
function.
Also we can already write many of these with even more general signatures without adding anything new to base:
Generalized maybeToList
is just toList
and is already in base
:
maybeToList :: Foldable f => f a -> [a]
maybeToList = toList
and then
catMaybes = foldMap toList
and that admits a much more general signature still:
catMaybes :: (Foldable f, Foldable g) => Foldable f => f (g a) -> [a]
Similarly:
mapMaybes :: (Foldable f, Foldable g) => (a -> f b) -> f a -> [b]
mapMaybes f = foldMap (toList . f)
The safeHead/listToMaybe generalization has some substance to it:
listToMaybe :: Foldable f => f a -> Maybe a
listToMaybe = getFirst . foldMap (First . Just)
None of these try to preserve the 'f' shape, because well, often you can't. e.g. filtering a NonEmpty
list can lead to an empty list.
There are also things like IntMap
where you do want to be able to remove values, but that typically requires some kind of ad hoc overloading specific to the data type involved, and so is probably better left to the province of libraries themselves to supply unlike these things above, which all just work out of the box already today.