Peter Popov
Peter Popov

Reputation: 662

Haskell way to join [IO String] into IO String

my goal is to write Haskell function which reads N lines from input and joins them in one string. Below is the first attempt:

readNLines :: Int -> IO String
readNLines n = do
  let rows = replicate n getLine
  let rowsAsString = foldl ++ [] rows 
  return rowsAsString  

Here haskell complaints on foldl:

Couldn't match expected type [a]' against inferred type(a1 -> b -> a1) -> a1 -> [b] -> a1'

As I understand type of rows is [IO String], is it possible some how join such list in a single IO String?

Upvotes: 4

Views: 4170

Answers (5)

gawi
gawi

Reputation: 14227

The shortest answer I can come up with is:

import Control.Applicative
import Control.Monad

readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine

Upvotes: 1

Bill
Bill

Reputation: 45476

replicate returns a list of IO String actions. In order to perform these actions, they need to be run in the IO monad. So you don't want to join an array of IO actions, but rather run them all in sequence and return the result.

Here's what I would do

readNLines :: Int -> IO String
readNLines n = do
  lines <- replicateM n getLine
  return $ concat lines

Or, in applicative style:

import Control.Applicative

readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine

Both of these use the monadic replicate (replicateM), which evaluates a list of monadic values in sequence, rather than simply returning a list of actions

Upvotes: 0

HaskellElephant
HaskellElephant

Reputation: 9891

The functions you are looking for is is sequence, however it should be noted that

sequence (replicate n f)

is the same as

replicateM n f

And foldl (++) [] is equivalent to concat. So your function is:

readNLines n = liftM concat (replicateM n getLine)

Alternatively if you want to preserve line breaks:

readNLines n = liftM unlines (replicateM n getLine)

Upvotes: 5

Daniel Pratt
Daniel Pratt

Reputation: 12077

Besides what ephemient points out, I think you have a syntax issue: The way you're using the ++ operator makes it look like you are trying to invoke the ++ operator with operands foldl and []. Put the ++ operator in parentheses to make your intent clear:

foldl (++) [] rows

Upvotes: 6

Related Questions