3

Say I have a List of integers l = [1,2]

Which I want to print to stdout.

Doing print l produces [1,2]

Say I want to print the list without the braces

map print l produces

No instance for (Show (IO ())) arising from a use of `print'
Possible fix: add an instance declaration for (Show (IO ()))
In a stmt of an interactive GHCi command: print it

`:t print

print :: Show a => a -> IO ()

So while I thought this would work I went ahead and tried:

map putStr $ map show l

Since I suspected a type mismatch from Integer to String was to blame. This produced the same error message as above.

I realize that I could do something like concatenating the list into a string, but I would like to avoid that if possible.

What's going on? How can I do this without constructing a string from the elements of the List?

Abraham P
  • 15,029
  • 13
  • 58
  • 126

5 Answers5

6

The problem is that

map :: (a -> b) -> [a] -> [b]

So we end up with [IO ()]. This is a pure value, a list of IO actions. It won't actually print anything. Instead we want

mapM_ :: (a -> IO ()) -> [a] -> IO ()

The naming convention *M means that it operates over monads and *_ means we throw away the value. This is like map except it sequences each action with >> to return an IO action.

As an example mapM_ print [1..10] will print each element on a new line.

daniel gratzer
  • 52,833
  • 11
  • 94
  • 134
4

Suppose you're given a list xs :: [a] and function f :: Monad m => a -> m b. You want to apply the function f to each element of xs, yielding a list of actions, then sequence these actions. Here is how I would go about constructing a function, call it mapM, that does this. In the base case, xs = [] is the empty list, and we simply return []. In the recursive case, xs has the form x : xs. First, we want to apply f to x, giving the action f x :: m b. Next, we want recursively call mapM on xs. The result of performing the first step is a value, say y; the result of performing the second step is a list of values, say ys. So we collect y and ys into a list, then return them in the monad:

mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f []       = return []
mapM f (x : xs) = f x >>= \y -> mapM f ys >>= \ys -> return (y : ys)

Now we can map a function like print, which returns an action in the IO monad, over a list of values to print: mapM print [1..10] does precisely this for the list of integers from one through ten. There is a problem, however: we aren't particularly concerned about collecting the results of printing operations; we're primarily concerned about their side effects. Instead of returning y : ys, we simply return ().

mapM_ :: Monad m => (a -> m b) ->[a] -> m ()
mapM_ f []       = return ()
mapM_ f (x : xs) = f x >> mapM_ f xs

Note that mapM and mapM_ can be defined without explicit recursion using the sequence and sequence_ functions from the standard library, which do precisely what their names imply. If you look at the source code for mapM and mapM_ in Control.Monad, you will see them implemented that way.

emi
  • 5,380
  • 1
  • 27
  • 45
2

Everything in Haskell is very strongly typed, including code to perform IO!

When you write print [1, 2], this is just a convenience wrapper for putStrLn (show [1, 2]), where show is a function that turns a (Show'able) object into a string. print itself doesn't do anything (in the side effect sense of do), but it outputs an IO() action, which is sort of like a mini unrun "program" (if you excuse the sloppy language), which isn't "run" at its creation time, but which can be passed around for later execution. You can verify the type in ghci

> :t print [1, 2] 
print [1, 2]::IO()

This is just an object of type IO ().... You could throw this away right now and nothing would ever happen. More likely, if you use this object in main, the IO code will run, side effects and all.

When you map multiple putStrLn (or print) functions onto a list, you still get an object whose type you can view in ghci

> :t map print [1, 2]
map print [1, 2]::[IO()]

Like before, this is just an object that you can pass around, and by itself it will not do anything. But unlike before, the type is incorrect for usage in main, which expects an IO() object. In order to use it, you need to convert it to this type.

There are many ways to do this conversion.... One way that I like is the sequence function.

sequence $ map print [1, 2]

which takes a list of IO actions (ie- mini "programs" with side effects, if you will forgive the sloppy language), and sequences them together as on IO action. This code alone will now do what you want.


As jozefg pointed out, although sequence works, sequence_ is a better choice here.... Sequence not only concatinates the stuff in the IO action, but also puts the return values in a list.... Since print's return value is IO(), the new return value becomes a useless list of ()'s (in IO). :)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
jamshidh
  • 12,002
  • 17
  • 31
  • That's not correct.. I think you meant `sequence $ map print` but why not `mapM` in this case – daniel gratzer Mar 05 '14 at 17:11
  • The missing `map` was just a retype error, I've fixed it. Other than that, I don't know why I get the downvote though, I use both `mapM` and `sequence` in my code, and I think `sequence` is both a useful and pedagogical alternative. It helps to think of each IO() action as a separate piece of code, which can be sequenced together. Although useful, I think of mapM as more of a clever way to make functional code look like imperative code. Don't get me wrong, I don't hate it, I just think it is less confusing to understand sequence first. – jamshidh Mar 05 '14 at 17:20
  • I -1'd because typo and because both `mapM` and `sequence` generate useless lists of `[()]`. I'd say that `mapM_` is actually the correct thing here. – daniel gratzer Mar 05 '14 at 17:32
  • I could also use `sequence_` instead of `sequence`.... I will add it in as a note at the end to avoid confusion to the main point though. – jamshidh Mar 05 '14 at 17:47
  • Note that the more idiomatic `mapM_ f xs` is in fact defined as `sequence_ (map f xs)` (see, e.g., [the library source code](http://hackage.haskell.org/package/base-4.6.0.1/docs/src/Control-Monad.html#mapM_)). There's also `forM_ xs f = mapM xs f`. – Luis Casillas Mar 05 '14 at 18:17
1

Using the lens library:

[1,2,3] ^! each . act print
Markus1189
  • 2,829
  • 1
  • 23
  • 32
0

You might write your own function, too:

Prelude> let l = [1,2]

Prelude> let f [] = return (); f (x:xs) = do print x; f xs

Prelude> f l 
1
2
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61