You seem to have a few things confused here. (In particular, lists form a monad, and I / O forms another monad.) I will try to clear this ...
First of all, the print function prints something indicative and writes it to the standard one, and then to a new line. So, print [1, 2, 3] works just fine, but obviously it writes everything on one line. To write material on separate lines, we need a separate print call for each element. So far so good.
The map function applies the function to each element of the list. Thus, map print [1, 2, 3] apply print to each element in the list. However, the result is a list of I / O actions. And this is not exactly what we need. We want to perform these actions, not list them.
The way to do this is to use the >> operator, which combines the two I / O actions (provided that they are not interested in their results), and printing something does not return anything interesting). So foldr (>>) (return ()) will take your list of I / O operations and turn it into a single I / O action. This function is actually already defined; it is called sequence .
However, map + sequence is such a common combination that it is also already defined; it is called mapM_ . (There is mapM , without underlining, if you want to save the results, but printing does not return anything, so there is no need.)
Now, why mapM_ works. Now you are asking why several other ways will not work ...
x <- [1, 2, 3] print x
This does not work at all. The first line is in the list of monads. But the second line is in the I / O monad. You cannot do this. (You will have a rather complex type-checking error.) I should probably point out that this is Haskell, the so-called "do-notation", and this snippet requires the do keyword on the front panel so that it really has strong syntax
do x <- [1, 2, 3] print x
In any case, this still does not work. It almost does what map print [1, 2, 3] does, but not quite. (As I said, he will not check the type.)
You also suggested [1, 2, 3] >>= print , which is identical to the previous snippet. (In fact, the compiler converts the first to the last.) The original does not check the type, and for the same reason, it does not check the type.
This is a bit like trying to add a number to a matrix. Numbers are extra things. Matrices are additional things. But you cannot add each other because they do not match. If that makes sense.