One recommendation you often hear when reaching an acceptable level of basic Haskell is to make your code more polymorphic. The Haskell Prelude is heavily biased towards lists, so an immediate gain in polymorphism is to make your code work not only for lists but for any instance of Traversable or Foldable.1

Since a Haskell list is an instance of Traversable and Foldable, we can still operate as usual on lists with the new polymorphic code:

>>> import qualified Data.Foldable as F
>>> mapM_ print ['a'..'c']
'a'
'b'
'c'
>>> F.traverse_ print ['a'..'c']
'a'
'b'
'c'

Notice here that we’ve gained the extra advantage of being able to use Applicative instead of Monad. Here, we don’t need the extra power of Monad, and the Applicative, by being weaker, is also more generalizable.

But aside of lists, there is another instance of Traversable/Foldable defined by default for us: Maybe. You could think of a Maybe as a list of 1 or 0 elements, so when you are traversing it you either do something with the element if present or do the default Applicative/Monad action (pure and return respectively) if not present. How is this useful then? Have you ever found yourself writing case expressions like this?

>>> :{
>>> let printM m = case m of
>>>                     Just n  -> print n
>>>                     Nothing -> return ()
>>> :}
>>> let m1 = Just 1 :: Maybe Int
>>> let m_ = Nothing :: Maybe Int
>>> printM m1
>>> 1
>>> printM m_
>>>

The function maybe would improve a bit, syntactically speaking: maybe (return ()) print.

Maybe it’s just me, but that return () smells too much of a default behavior to me. Somehow, there should be a way to avoid it. Well, here is where Foldable instance of Maybe comes handy:

>>> :set -XScopedTypeVariables
>>> let printM' :: Maybe Int -> IO () = F.traverse_ print
>>> printM' m1
>>> 1
>>> printM' m_
>>>

To be fair, for this trivial example, it would be a bit frivolous to use the Maybe Foldable instance just to avoid the case expression, but when you are in an intricate case expression ladder, this idiom can make your code much more readable.

  1. Roughly speaking, the Traversable instance would be used for operations that do something on each element of the structure while maintaining same structure in the output. A Foldable instance would be used for collapsing the elements into anything else.