I'm going to give a mini monad tutorial here. Hopefully that will also clear up at least some of the confusion in the other threads here.

A monad is a container type with at least these three operations:

1. unit(x): Create a container with a single element x inside.

2. map(f,c): Given a container c with elements, call the function f on each element, and create a new container out of the results.

3. flatten(c): Given a container of containers, we can flatten it down by putting all the elements in the nested containers in a single container.

For example, a List<T> is a monad:

1. unit(x) creates a list with a single element.

2. map(f,list) calls f on each element of the list, and creates a new list out of the results.

3. flatten(listoflists) flattens the nested lists down. Note that flatten only flattens away one level, so if you have a list of lists of lists, then flattening it will get you a list of lists.

These operations have to have some laws, like:

1. map(f, unit(x)) = unit (f x): if we map a function over a container with a single element, we just call the function on that single element and put it inside its own container.

2. flatten(unit(c)) = c: if we wrap a container c in its own single element container unit(c) then flattening it gives us back the original.

3. flatten(map(unit,c)) = c: if we take each element of a container and wrap each one in its own container, and then flatten the result, we get back the original.

Turns out that you can also define these operations for sets and multisets. The interesting thing is that there are more things that fit in the API above. Futures, parsers, probability distributions, I/O operations, mutable state, dynamic scoping, exceptions, optional values, logic programming, continuations, asynchronous programming, and more. Monads are what all those things have in common. I think the only way, ultimately, to learn how monads work is to study many examples. You may ask yourself, for example, what does it mean to flatten a parser? Turns out you can give a meaningful definition for flattening a parser. If you study many such examples, you start to get the pattern.

What makes monads so neat to use is that you can define special syntax for it. For example list comprehensions can be translated to the operations above, which means that list comprehensions also work on all the other things that are monads. So you get "parser comprehensions" and "futures comprehensions". Similarly, mutable state has special concise syntax in imperative languages, but with monads that syntax also works for lists, probability distributions, etc. That's what do-notation is in Haskell.

I hope this helps somebody get interested in what monads really are about.

That said, to use I/O in Haskell you do not need to understand any of this. If you can read this:

do print "What's your name?"
name <- readLn
print ("Hello, " ++ name)

Then you're good to go. You do not need to know that there is a I/O monad underneath this, you can just do like you're coding in an imperative language. The only difference is that the syntax above is not restricted to I/O and mutable state like it is in most languages, but it can also be used for creating parsers, manipulating lists, etc.

For example, the assignment operator in the I/O monad means assignment, but in the list monad it means "take each element of":

do x <- [1,2,3]
y <- [4,5,6]
return (x*y)

This creates the list

[1*4,1*5,1*6, 2*4,2*5,2*6, 3*4,3*5,3*6].