Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print list elements in new lines

I'm just totally confused with lists and monads, so maybe my question isn't correct or very naive. I've seen the way to do it using mapM_ func here:

mapM_ print [1, 2, 3, 4]

But I don't know exactly how it works and want to know how can I do this in a way like this:

x <- [1, 2, 3]
print x

or, if I understood it right:

[1, 2, 3] >>= print

I understand that [1, 2, 3] has type [a] and print has type Show a => a -> IO (). Also I understand that for using monad List we need type List a on the left and func with type a -> List b on the right. Am I right? Can you help me with this?

UPD. Thanks @MathematicalOrchid for explanation how mapM_ works. From my side I want to explain that the real problem is not printing any results in different lines but do some monadic actions(because now I'm hanging around OpenGL stuff) in a way monad List provides it. But I got that the root of misunderstanding was in mixing monads.

UPD2. Thanks everyone for answers. I apologize for this kinda fuzzy question. I dind't exactly know what answer I need and what is the question. It's because I didn't understand some basics. So it's hard to choose "the correct answer" now because every answers have a small peace of what I was looking for. I've decided to choose the closest(although not the most useful now) to what I wanted.

like image 743
pkuderov Avatar asked Nov 28 '22 04:11

pkuderov


2 Answers

You seem to have several things confused here. (In particular, lists form a monad, and I/O forms a different monad.) I will try to clear this up...

First of all, the print function takes anything showable and writes it to standard out, followed by a newline. So print [1, 2, 3] works just fine, but obviously writes everything on the same line. To write stuff on seperate lines, we need a seperate invocation of print for each item. So far, so good.

The map function applies a function to every element of a list. So map print [1, 2, 3] would apply print to each item in the list. However, the result is a list of I/O actions. And that's not quite what we're after. We want to perform these actions, not list them.

The way to do this is to use the >> operator, which chains two I/O actions together (provided you're not interested in their results - and printing something doesn't return anything interesting). So foldr (>>) (return ()) will take your list of I/O actions and turn it into one single I/O action. This function is in fact already defined; it's called sequence.

However, map + sequence is such a common combination that this too is also already defined; it's called mapM_. (There's also mapM, without the underscore, if you wanted to keep the results. But printing doesn't return anything, so there's no need.)


Now, that's why mapM_ works. Now you ask why several other ways won't work...

x <- [1, 2, 3]
print x

This doesn't work at all. The first line is in the list monad. But the second line is in the I/O monad. You can't do that. (You will get a rather baffling type checker error.) I should probably point out that this is Haskell's so-called "do-notation", and the above fragment needs the do keyword at the front for it to actually be valid syntax:

do
  x <- [1, 2, 3]
  print x

Either way, it still doesn't work. It almost does what map print [1, 2, 3] does, but not quite. (As I said, it won't type-check.)

You also suggested [1, 2, 3] >>= print, which is identical to the previous snippet. (In fact, the compiler converts the former to the latter.) The original doesn't type-check, and this doesn't type-check either, for the same reason.

It's a bit like trying to add a number to a matrix. Numbers are addable things. Matricies are addable things. But you cannot add one to the other because they aren't the same. If that makes sense.

like image 70
MathematicalOrchid Avatar answered Dec 05 '22 03:12

MathematicalOrchid


What you want cannot work this way since you are trying to mix two monads together:

do x <- [1,2,3]
   print x

Specifically you are mixing the IO and the [] monads. In do-notation, all the statements should have the type m a for some Monad m. But in the above code, the first statement has the type [Integer] while the second statement has the type IO ().

To get the effect you want you should use the ListT monad transformer. Monad transformers allow mixing monads together in a certain order in a stack and combining their effects as needed.

import Control.Monad.Trans
import Control.Monad.Trans.List

value = do x <- ListT (return [1,2,3])
           lift (print x)

This will return a value of type ListT IO Integer. To get the IO computation out of this transformer, use runListT. Which will return a value of type IO [Integer]. This will output:

GHCI> runListT value
1
2
3
[(),(),()]

Which is equivalent to mapM print [1,2,3]. To throw away the list and get the effect of mapM_ print [1,2,3] you can use void from Control.Monad.

GHCI> void . runListT $ value
1
2
3
like image 38
is7s Avatar answered Dec 05 '22 02:12

is7s