Other answers here have rightfully indicated that functors don't allow for side effects because they cannot be combined or sequenced, which is quite true at large, but there is one way to sequence functors: by going inward.
Let's write a limited Writer functor.
data Color = R | G | B
data ColorW a = Re a | Gr a | Bl a deriving (Functor)
and then apply the Free monad type to it
data Free f a = Pure a | Free (f (Free f a))
liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure
type ColorWriter = Free ColorW
red, blue, green :: a -> ColorWriter a
red = liftF . Re
green = liftF . Gr
blue = liftF . Bl
Of course, by the free property, this forms a monad, but the effects are really coming from the "layers" of the functor.
interpretColors :: ColorWriter a -> ([Color], a)
interpretColors (Pure a) = ([], a)
interpretColors (Free (Re next)) = let (colors, a) = interpretColors next
in (R : colors, a)
...
So, this is sort of a trick. Really the "computation" is being introduced by the free monad but the material of the computation, the hidden context, is introduced by just a functor. It turns out you can do this with any data type, it need not even be a Functor, but Functor provides a clear way to build it.
fmap (unlines.reverse.lines) $ readFile "chronological_log.txt"
. I tend to use the infix operator forfmap
,<$>
imported fromData.Functor
(orControl.Applicative
!) so I'd normally writeunlines.reverse.lines <$> readFile "chronological_log.txt"
. – Fritzie