4

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?

5 Answers 5

20

You're looking for sequence :: (Monad m) => [m a] -> m [a].

(Plus liftM :: Monad m => (a1 -> r) -> m a1 -> m r and unlines :: [String] -> String, probably.)

Sign up to request clarification or add additional context in comments.

Comments

6

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

2 Comments

Ah yes, that is the immediate cause of the typecheck error… that I ignored, since even after OP fixes that they've still got another problem.
Should also note that foldl (++) [] is the same as concat.
5

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)

2 Comments

concat is foldr (++) [], not foldl: works fine on infinite streams of lists. You're missing n as an argument to replicateM in your later examples there, too. But yes, those are good points.
@ephemient, thanks for the heads up, I fixed the error with n, and yes concat is really foldr (++) [], what I mean was that he wrote foldl (++) [] wich is equivalent to concat, although as you mention, concat is actually better since it works on infinite streams.
1

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.