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 main
.
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. Functor
, Applicative
and Monad
just give us tools what we can do with them (Functor
s allow to modify values inside, Applicative
s allow to combine effects and Monad
s allow effects to depend on the previous values). But in the context of Monad
s, 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".
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…