FP Complete

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:

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.

concat, mconcat and fold

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:

concatMap and foldMap

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.

*> and >>

(*>) :: 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 and return

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, fmap and liftM

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_ and mapM_

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_ and sequence_

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

Same Monad/Applicative specialization.

traverse and mapM

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 and sequence

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 and forM

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.

<>, ++ and mappend

(++) :: [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.

Subscribe to our blog via email

Email subscriptions come from our Atom feed and are handled by Blogtrottr. You will only receive notifications of blog posts, and can unsubscribe any time.