Это хорошо масштабируется. Однако в приложении, над которым я сейчас работаю, мы используем более длинные имена файлов (я думаю, это 20 символов), чтобы значительно увеличить пространство имен и уменьшить вероятность коллизий.
В строке, где ошибка происходит, Haskell ожидает "IO", но Вы даете его []. Упрощение вещей много, на действительно блокируется на монаде IO, каждая строка также:
В этом действительно блокируются, "hGetLine inh" возвращает "Строку IO", и Строка в нем извлечена и дана имя inpStr. Следующая строка, так как это ни один позволенный или <-, должна иметь тип "IO", который это не делает (таким образом порождение ошибки компилятора). То, что можно сделать вместо этого, так как у Вас уже есть Строка, является позволенным:
let list' = inpStr:list
Это создает новый список, состоящий из Строки, сопровождаемой исходным списком, и дает ему название "списка'".
Измените следующую строку для использования "списка'" вместо "списка" (таким образом передающий его новый список). Та строка вызовы (рекурсивно) mainloop, который считает еще одну строку, называет себя и так далее. После чтения целого файла это возвратит что-то с "IO ()" тип. Это "IO ()" будет возвращено к, действительно блокируются в loadNums. Поздравления, Вы просто создали список со строками, считанными из файла, в обратном порядке (так как Вы добавляли к заголовку списка), и затем ничего не сделал к нему.
Если Вы хотите сделать что-то к нему, изменить "возврат ()" для "возврата списка"; возврат сгенерирует значение типа "IO [Строка]", со списком в нем (возврат делает не что иное как инкапсуляцию значения), который можно извлечь в loadNums с <-синтаксис.
Остальное оставляют как осуществление читателю.
Если это не для домашней работы или чего-то, нет никакой причины использовать такое усилие. Повторное использование лениво!
getLines = liftM lines . readFile
main = do
list <- getLines "dictionary.txt"
mapM_ putStrLn list
Но поскольку Вы, кажется, все еще изучаете Haskell, для Вас важно понять то, что записал CesarB.