Why are side-effects modeled as monads in Haskell?

Suppose a function has side effects. If we take all the effects it produce as the input and output parameters, then the function is pure to the outside world.

Suppose a function has side effects. If we take all the effects it produce as the input and output parameters, then the function is pure to the outside world. So for an impure function f' :: Int -> Int we add the RealWorld to the consideration f :: Int -> RealWorld -> (Int, RealWorld) -- input some states of the whole world, -- modify the whole world because of the a side effects, -- then return the new world.

Then f is pure again. We define a parametrized data type IO a = RealWorld -> (a, RealWorld), so we don't need to type RealWorld so many times f :: Int -> IO Int To the programmer, handling a RealWorld directly is too dangerous—in particular, if a programmer gets their hands on a value of type RealWorld, they might try to copy it, which is basically impossible. (Think of trying to copy the entire filesystem, for example.

Where would you put it? ) Therefore, our definition of IO encapsulates the states of the whole world as well. These impure functions are useless if we can't chain them together.

Consider getLine :: IO String = RealWorld -> (String, RealWorld) getContents :: String -> IO String = String -> RealWorld -> (String, RealWorld) putStrLn :: String -> IO () = String -> RealWorld -> ((), RealWorld) We want to get a filename from the console, read that file, then print the content out. How would we do it if we can access the real world states? PrintFile :: RealWorld -> ((), RealWorld) printFile world0 = let (filename, world1) = getLine world0 (contents, world2) = (getContents filename) world1 in (putStrLn contents) world2 -- results in ((), world3) We see a pattern here: the functions are called like this: ... (, worldY) = f worldX (, worldZ) = g worldX ... So we could define an operator ~~~ to bind them: (~~~) :: (IO b) -> (b -> IO c) -> IO c (~~~) :: (RealWorld -> (b, RealWorld)) -> (b -> RealWorld -> (c, RealWorld)) -> RealWorld -> (c, RealWorld) (f ~~~ g) worldX = let (resF, worldY) = f worldX in g resF worldY then we could simply write printFile = getLine ~~~ getContents ~~~ putStrLn without touching the real world.

Now suppose we want to make the file content uppercase as well. Uppercasing is a pure function upperCase :: String -> String But to make it into the real world, it has to return an IO String. It is easy to lift such a function: impureUpperCase :: String -> RealWorld -> (String, RealWorld) impureUpperCase str world = (upperCase str, world) this can be generalized: impurify :: a -> IO a impurify :: a -> RealWorld -> (a, RealWorld) impurify a world = (a, world) so that impureUpperCase = impurify .

UpperCase, and we can write printUpperCaseFile = getLine ~~~ getContents ~~~ (impurify . UpperCase) ~~~ putStrLn (Note: Normally we write getLine ~~~ getContents ~~~ (putStrLn . UpperCase)) Now let's see what we've done: We defined an operator (~~~) :: IO be -> (b -> IO c) -> IO c which chains two impure functions together We defined a function impurify :: a -> IO a which converts a pure value to impure.

Now we make the identification (>>=) = (~~~) and return = impurity, and see? We've got a monad. (To check whether it's really a monad there's few axioms should be satisfied: (1) return a >>= f = f a impurify a = (\world -> (a, world)) (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world)) worldX in f resF worldY = let (resF, worldY) = (a, worldX)) in f resF worldY = f a worldX (2) f >>= return = f (f ~~~ impurify) a worldX = let (resF, worldY) = impuify a worldX in f resF worldY = let (resF, worldY) = (a, worldX) in f resF worldY = f a worldX (3) f >>= (\x -> g x >>= h) = (f >>= g) >>= h Exercise.).

7 This is the best explanation I have ever read. Thank you for writing it! Ps.

Can you please edit your code examples so that they weren't all on a single line? – bodacydo Mar 21 '10 at 23:01 1 +1: Hell, sooo good answer on rather complex topic. – gorsky Mar 22 '10 at 8:24 1 +1 but I want to note that this specifically covers the IO case.Blog.sigfpe.Com/2006/08/you-could-have-invented-monads-and.

Html is pretty similar, but generalizes RealWorld into... well, you'll see. – ephemient Mar 22 '10 at 16:46 Whoah! I need to read this again.

– Damian Powell Apr 24 '10 at 10:14 2 Note that this explanation cannot really apply to Haskell's IO, because the latter supports interaction, concurrency, and nondeterminism. See my answer to this question for some more pointers. – Conal Aug 16 at 0:23.

This question contains a widespread misunderstanding. Impurity and Monad are independent notions. Impurity is not modeled by Monad.

Rather, there are a few data types, such as IO, that represent imperative computation. And for some of those types, a tiny fraction of their interface corresponds to the interface pattern called "Monad". Moreover, there is no known pure/functional/denotative explanation of IO (and there is unlikely to be one, considering the "sin bin" purpose of IO), though there is the commonly told story about World -> (a, World) being the meaning of IO a.

That story cannot truthfully describe IO, because IO supports concurrency and nondeterminism. The story doesn't even work when for deterministic computations that allow mid-computation interaction with the world. For more explanation, see this answer.

Edit: On re-reading the question, I don't think my answer is quite on track. Models of imperative computation do often turn out to be monads, just as the question said. The asker might not really assume that monadness in any way enables the modeling of imperative computation.

But GHC does define IO as newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)) (Ref: haskell. Org/ghc/docs/latest/html/libraries/ghc-prim-0.2.0.0/src/…) where State# is a thread-local type. – KennyTM Aug 16 at 4:55 @KennyTM: But RealWorld is, as the docs say, "deeply magical".

It's a token that represents what the runtime system is doing, it doesn't actually mean anything about the real world. You can't even conjure up a new one to make a "thread" without doing extra trickery; the naive approach would just create a single, blocking action with a lot of ambiguity about when it will run. – C.A.McCann Aug 16 at 13:03 Also, I would argue that monads are essentially imperative in nature.

If the functor represents some structure with values embedded in it, a monad instance means you can build and flatten new layers based on those values. So whatever meaning you assign to a single layer of the functor, a monad means you can create an unbounded number of layers with a strict notion of causality going from one to the next. Specific instances may not have intrinsically imperative structure, but Monad in general really does.

– C.A. McCann Aug 16 at 13:19 KennyTM. Yes, I suspect that many are misled by this "definition". It could not really be the definition of IO for the reasons I've given.

Rather it is part of a definition, the rest of which lives in the compiler internals, which I think is what some people mean by "deeply magical". – Conal Aug 17 at 7:25 C.A. McC: I don't know what you mean by "Specific instances may not have intrinsically imperative structure, but Monad in general really does. " (Does in general but not in particular?) Nor what collapsing functor nesting has to do with imperative computation.

Maybe "imperative" has a meaning for you that I wouldn't expect. – Conal Aug 17 at 7:57.

As I understand it, someone called Eugenio Moggi first noticed that a previously obscure mathematical construct called a "monad" could be used to model side effects in computer languages, and hence specify their semantics using Lambda calculus. When Haskell was being developed there were various ways in which impure computations were modelled (see Simon Peyton Jones' "hair shirt" paper for more details), but when Phil Wadler introduced monads it rapidly became obvious that this was The Answer. And the rest is history.

Well, because Haskell is pure. You need a mathematical concept to distinguish between unpure computations and pure ones on type-level and to model programm flows in respectively. This means you'll have to end up with some type IO a that models an unpure computation.

Then you need to know ways of combining these computations of which apply in sequence (>>=) and lift a value (return) are the most obvious and basic ones. With these two, you've already defined a monad (without even thinking of it);) In addition, monads provide very general and powerful abstractions, so many kinds of control flow can be conveniently generalized in monadic functions like sequence, liftM or special syntax, making unpureness not such a special case. See monads in functional programming and uniqueness typing (the only alternative I know) for more information.

It's actually quite a clean way to think of I/O in a functional way. In most programming languages, you do input/output operations. In Haskell, imagine writing code not to do the operations, but to generate a list of the operations that you would like to do.

Monads are just pretty syntax for exactly that. If you want to know why monads as opposed to something else, I guess the answer is that they're the best functional way to represent I/O that people could think of when they were making Haskell.

AFAIK, the reason is to be able to include side effects checks in the type system. If you want to know more, listen to those SE-Radio episodes: Episode 108: Simon Peyton Jones on Functional Programming and Haskell Episode 72: Erik Meijer on LINQ.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions