replicate
возвращает список IO String
действий.Для выполнения этих действий их нужно запустить в монаде IO.Поэтому вы не хотите присоединяться к массиву действий ввода-вывода, а просто запускать их все по порядку и возвращать результат.
Вот что я бы сделал
readNLines :: Int -> IO String
readNLines n = do
lines <- replicateM n getLine
return $ concat lines
Или в аппликативномstyle:
import Control.Applicative
readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine
Оба они используют монадическую реплику (replicateM), которая оценивает список монадических значений в последовательности, а не просто возвращает список действий