A side effect is an observable interaction with its environment (apart from computing its result value). In Haskell, we try hard to avoid functions with such side effects. This even applies to
IO actions: when an
IO action is evaluated, no side effects are performed, they are executed only when the actions prescribed in the
IO value are executed within
However, when working with abstractions that are related to composing computations, such as applicative functors and monads, it’s convenient to somewhat distinguish between the actual value and the “rest”, which we often call an “effect”. In particular, if we have a type
f of kind
* -> *, then in
f a the
a part is “the value” and whatever “remains” is “the effect”.
I intentionally quoted the terms, as there is no precise definition (as far as I know), it’s merely a colloquial definition. In some cases there are no values at all, or multiple values. For example for
Maybe the “effect” is that there might be no value (and the computation is aborted), for
 the “effect” is that there are multiple (or zero) values. For more complex types this distinction can be even more difficult.
The distinction between “effects” and “values” doesn’t really depend on the abstraction.
Monad just give us tools what we can do with them (
Functors allow to modify values inside,
Applicatives allow to combine effects and
Monads allow effects to depend on the previous values). But in the context of
Monads, it’s somewhat easier to create a mental picture of what is going on, because a monadic action can “see” the result value of the previous computation, as witnessed by the
(>>=) :: m a -> (a -> m b) -> m b
operator: The second function receives a value of type
a, so we can imagine “the previous computation had some effect and now there is its result value with which we can do something”.