From what I gathered from learning Haskell, functional programming limits the amount of side effects, but in what ways? Hope someone can enlighten me on this one.


3 Answers 3


There are two different definitions of the term "functional programming". But neither of the two limits side-effects.

The original definition was "programming with first-class and higher-order subroutines". This was the definition applied to LISP, Scheme, ML, and many others. Nowadays, almost every language (with the exception of C) has first-class and higher-order subroutines, the last major holdouts were Java and C++. So, this definition is less useful than it used to be, since almost every language fulfills it.

So, later a new definition became popular: "programming without side-effects". This is the definition which is used in Haskell, for example.

However, neither of the two definitions limits side-effects. The first definition doesn't say anything about side-effects at all (and early LISP as well as many of its descendants, e.g. CommonLisp are very much imperative). And the second definition doesn't limit side-effects, it simply forbids them entirely.

  • William Cook wrote an interesting article where he traces the mutation of the definition from its original meaning to its current one by tracing the edit history of the Wikipedia article on functional programming: wcook.blogspot.com/2012/07/… Commented Nov 18, 2018 at 22:49
  • How can a programming language perform I/O without any side effects?
    – Code-Guru
    Commented Nov 19, 2018 at 15:48
  • 4
    It can't. It can, however, model I/O. Commented Nov 19, 2018 at 16:49
  • 2
    @Code-Guru your program returns another program that can do I/O. Your main returns a command that says to read a line from the user, and a function that will return the next command after that, depending on what the user types. Commented Nov 20, 2018 at 22:37

Haskell is a lazy language. A piece of code is only executed if its value is used. This means that if we want to cause side effects in a specific sequence, we must model the order of effects as a value where the later effects require the value of previous effects.

In an imperative language like C, we can simply use semicolons to sequence statements:


In Haskell, to sequence the effects of printing a value, we must express this through the data flow. Something like:

forceEvaluationInSequence [firstEffect, secondEffect]
  where firstEffect = putStr "foo"
        secondEffect = putStr "bar"

These values symbolizing external effects are represented in Haskell as the IO monad. The main function of a program returns an IO value. The >> operator can be used for sequencing between monads:

main = putStr "foo" >> putStr "bar"

Because monads are central to Haskell they have special syntax: the do-notation. This code is equivalent to the above:

main = do
  putStr "foo"
  putStr "bar"

(Because of the similarity in notation to imperative programming, monads are sometimes called “programmable semicolons”.)

So in Haskell, there are no longer side effects. An effect is modeled explicitly as a value. This aligns well with the idea of substitutability/equational reasoning: in a pure functional language, I can replace the expression with the value of that expression.


Haskell code, by default, does not produce or rely on side effects. If you want to write code that uses side effects, it has to be written in a different imperative style and its type has to explicitly declare that side effects are being performed within it.

This is all done through the type system. All library functions that produce side effects and a result of type a have a result type of IO a. Pure code has no way to take the IO wrapper off of this value produced by the library function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.