Question

Hi I have small problem with printing [[[Char]]] using IO.

I have a [[[Char]]] list - in another words [[String]]

I would like to print them like

FirstStringOfFirstListOfStrings
SecondStringOfFirstListOfStrings
.
.
.
LastStringOffFirstListofStrings
(some gap anything empty line "----" string anything)
FirstStringOfSecondsListOfStrings

In another words I don't want the [String] to be printed one under another (one row break at least or some line filled with ------ for example)

mapM_ (mapM_ print) 

My idea was to compose the (mapM_ print) function with another but that doesn't seem to work. Any suggestions?

Edit: Here's an example, as requested

data to print: [["abc","cde","efg"],["hjk","mno","prs"],["stu","wxy","zzz"]]

while using mapM_ (mapM_ print) it prints like

abc
cde
efg
hjk
mno
prs
stu
wxy
zzz

But I would like it to be printed like this:

abc
cde
efg

hjk
mno
prs

stu
why
zzz

They can be separated by an empty line or some string.

Was it helpful?

Solution

Lets start with types. What you have is [[String]] and what you want is [[IO ()]] i.e each String gets converted to a IO () action (to print it). Lets try to achieve this:

strToPrint :: [[String]] -> [[IO ()]]
strToPrint strs = map (map putStrLn) strs                

Now we want [[IO ()]] to flatten to get [IO ()] where we insert the desired line break print action as well while flattening the list.

flattenActions :: [[IO ()]] -> [IO ()]
flattenActions actions = concat $ intersperse [(putStrLn "")]  actions

Now we can execute these [IO ()] action one by one using sequence_. It seems we can combine all these functions into one function using functional composition and hence we get

printStrings :: [[String]] -> IO ()
printStrings  = sequence_ . flattenActions . strToPrint

And use it like:

main = printStrings [["a","b","c"],["d","e","f"]]

You can then look for making this more generic and refactor it accordingly.

OTHER TIPS

With your example the code, for any separator 'sep' is then:

import Data.List (intercalate)

let x = [["abc","cde","efg"],["hjk","mno","prs"],["stu","wxy","zzz"]]

p sep = putStr . unlines . intercalate sep

main = do
    p "" x
    p "---" x

Remember that mapM_ takes a function that returns a monadic action as its parameter. That action can be composed using the normal monadic operations.

So in your code sample you have:

printStrings :: [[String]] -> IO ()
printStrings = mapM_ (mapM_ putStrLn)

Whereas you would like to have:

printStrings :: [[String]] -> IO ()
printStrings = mapM_ (\xs -> (mapM_ putStrLn) xs >> putStrLn ""))

So what we do is to replace the inner mapM_ putStrLn with a function: \xs -> mapM_ (putStrLn xs >> putStrLn "") that also prints out the separator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top