I'm feeling rather silly asking this question, but it's been on my mind for a while and I can't find any answers.
So the question is: why can applicative functors have side effects, but functors can't?
Maybe they can and I've just never noticed...?
It is not true that
Functor
s don't have effects. EveryApplicative
(and everyMonad
throughWrappedMonad
) is aFunctor
. The main difference is thatApplicative
andMonad
give you tools how to work with those effects, how to combine them. RoughlyApplicative
allows you to sequence effects and combine values inside.Monad
in addition allows you to determine a next effect according to the result of a previous one.However
Functor
only allows you to modify the value inside, it doesn't give tools to do anything with the effect. So if something is justFunctor
and notApplicative
, it doesn't mean it doesn't have effects. It just doesn't have a mechanism how to combine them in this way.Update: As an example, consider
This is clearly a
Functor
instance that carries effects. It's just that we don't have a way how to define operations with these effects that would comply toApplicative
. Unless we impose some additional constraints onr
, there is no way how to define anApplicative
instance.This answer is a bit of an over-simplification, but if we define side effects as computations being affected by previous computations, it's easy to see that the
Functor
typeclass is insufficient for side effects simply because there is no way to chain multiple computations.The only thing a functor can do is to alter the end result of a computation via some pure function
a -> b
.However, an applicative functor adds two new functions,
pure
and<*>
.The
<*>
is the crucial difference here, since it allows us to chain two computations:f (a -> b)
(a computation which produces a function) andf a
a computation that provides the parameter to which the function is applied. Usingpure
and<*>
it's possible to define e.g.Which simply chains two computations, discarding the end result from the first one (but possibly applying "side effects").
So in short, it's the ability to chain computations which is the minimum requirement for effects such as mutable state in computations.
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.
and then apply the Free monad type to it
Of course, by the free property, this forms a monad, but the effects are really coming from the "layers" of the functor.
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.
Let's first rename side effects to effects. All kinds of values can have effects. A functor is a type that allows you to map a function over whatever is produced by that effect.
When a functor is not applicative it doesn't allow you to use a certain composition style for the effects. Let's pick a (contrived) example:
This is easily a functor:
However, there is no sensible implementation for
pure
, so this type is not an applicative functor. It is similar toMaybe
in that it has the effect that there may not be a result value. But you can't compose it using applicative combinators.