Share this:

There are a number of cases in the base package where the same functionality exists under two different names. This can occur for a number of different reasons, but most commonly it’s either:

  • One version is more general than the other via some typeclass
  • A historical name has lingered on

The presence of identical (or nearly identical) functionality under different names can lead to confusion when reading code. The purpose of this page is to point out these occurrences to bypass this confusion.

GHC.OldList.concat :: [[a]] -> [a]
Prelude.concat :: Foldable t => t [a] -> [a]
mconcat :: Monoid a => [a] -> a
fold :: (Foldable t, Monoid a) => t a -> a

All four of these functions allow us to collapse down a sequence of values into a single value. The most specific is GHC.OldList.concat: given a list of lists, it combines all of these lists together into a single list. The most general is fold, which leverages two typeclasses:

  • Instead of requiring that the containers be a list, it leverages the
    Foldable typeclass to work with many more data structures
  • Instead of requiring that the values being combined be lists, it
    generalizes to any instance of Monoid
GHC.OldList.concatMap :: (a -> [b]) -> [a] -> [b]
Prelude.concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
foldMap :: (Foldable t, Monoid b) => (a -> b) -> t a -> b

This is very similar to the concat/mconcat/fold breakdown above. We can generalize from lists to instances of Foldable and
Monoid.

(*>) :: Applicative f => f a -> f b -> f b
(>>) :: Monad m => m a -> m b -> m b

The only difference between these two is Applicative vs
Monad. This is a holdover from the days when Applicative was not a superclass of Monad.

pure :: Applicative f => a -> f a
return :: Monad m => a -> m a

return is pure specialized to Monad, relevant for the same superclass reason above.

map :: (a -> b) -> [a] -> [b]
fmap :: Functor f => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b

map is specialized to just lists, while fmap is generalized to all
Functors. Like *> vs >>, the presence of liftM is just a
holdover from the days when Functor was not a superclass of Monad.

traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()

mapM_ is traverse_ specialized to Monad, relevant for the same superclass reason above.

sequenceA_ :: (Foldable t, Applicative f) => t (f a) -> f ()
sequence_ :: (Foldable t, Monad m) => t (m a) -> m ()

Same Monad/Applicative specialization.

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)

Same Monad/Applicative specialization.

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)

Same Monad/Applicative specialization.

for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)

Same Monad/Applicative specialization.

(++) :: [a] -> [a] -> [a]
Data.Semigroup.(<>) :: Semigroup a => a -> a -> a
mappend :: Monoid m => m -> m -> m

++ is simply <> and mappend specialized to lists. mappend should be the same as <> in all cases.

Note: historically, Semigroup was not a superclass of Monoid, resulting in <> and mappend having potentially different implementations. That no longer applies.