Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iteratively printing every integer in a List

Tags:

haskell

ghc

ghci

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?

like image 829
Abraham P Avatar asked Jan 27 '26 17:01

Abraham P


2 Answers

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.

like image 141
Daniel Gratzer Avatar answered Jan 29 '26 13:01

Daniel Gratzer


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.

like image 27
emi Avatar answered Jan 29 '26 12:01

emi