• You have a producer of Cs, then you can turn it into a producer of Ds by post-processing its output with a function g: C → D.
• You have a consumer of Bs, then you can turn it into a consumer of As by pre-processing its input with a function f: A → B.
• You have something that consumes Bs and produces Cs, then you can turn it into something that consumes As and produces Ds using two functions f: A → B and g: C → D.
• You have a producer of Cs, then you can turn it into a producer of Ds by post-processing its output with a function g: C → D.
• You have a consumer of Bs, then you can turn it into a consumer of As by pre-processing its input with a function f: A → B.
• You have something that consumes Bs and produces Cs, then you can turn it into something that consumes As and produces Ds using two functions f: A → B and g: C → D.
With pictures: http://mez.cl/prodcons.png
If you understand that, you understand functors:
• producer = (covariant) functor;
• consumer = contravariant functor;
• producer-consumer = invariant functor;
• post-processing = map;
• pre-process = contramap;
• pre- and post-processing = xmap (in Scala), invmap (in Haskell);
• defining how the pre- and post-processing works for a given producer or consumer = declaring a typeclass instance.
It doesn't mean that "a functor is a producer", but the mechanics are the same.