Everything in Haskell is very heavily typed, including code to execute IO!
When you write print [1, 2] , this is just a convenient wrapper for putStrLn (show [1, 2]) , where show is a function that turns an object (Showable) into a string. print itself does nothing (in the sense of a side effect of do), but it produces an IO() action, which is similar to the unrun mini-program (if you excuse the sloppy language), which is not 't "run" at the time of its creation, but which can be transferred for subsequent execution. You can check the type in ghci
> :t print [1, 2] print [1, 2]::IO()
It's just an object like IO () .... You can throw it away right now and nothing will ever happen. Rather, if you use this object in main , I / O code, side effects and thatβs it.
When you map several putStrLn (or print ) functions in a list, you still get an object whose type you can view in ghci
> :t map print [1, 2] map print [1, 2]::[IO()]
As before, it is just an object that you can go through and will not do anything by itself. But unlike the previous one, the type is incorrect for use in main, which the IO () object expects. To use it, you need to convert it to this type.
There are many ways to do this conversion ... One of the ways that I like is the sequence function.
sequence $ map print [1, 2]
which takes a list of I / O operations (that is, mini-programs with side effects if you forgive a sloppy language) and combines them sequentially, as with an I / O operation. Only this code will do what you want.
As jozefg noted, although sequence works, sequence_ is the best choice here .... A sequence not only concatenates the material in an I / O action, but also puts the return values ββin a list .... Since the print return value is IO (), the new the return value becomes a useless list () (in IO). :)