Haskell: вставка каждой строки из файла в список

В настоящее время я работаю над проектом с Haskell, и у меня возникли проблемы. Я должен читать и вставлять в список каждую строку в файле "dictionary.txt", но я не могу этого сделать. У меня есть этот код:

main = do
    let list = []
    loadNums "dictionary.txt" list

loadNums location list = do
    inh <- openFile location ReadMode
    mainloop inh list
    hClose inh

mainloop inh list = do 
    ineof <- hIsEOF inh
    if ineof
        then return ()
        else do 
            inpStr <- hGetLine inh
            inpStr:list
            mainloop inh list

Предполагается получить каждую строку (я знаю, что она получает каждую строку, так как замена "inpStr: list" на "putStrLn inpStr" работает корректно, отображает все строки) и вставляет ее в список, но я получаю следующая ошибка:

Couldn't match expected type `IO' against inferred type `[]'

Вероятно, потому что hGetLine не является строкой, а строкой ввода-вывода, о которой я понятия не имею, как обращаться, чтобы получить правильную строку, которую я могу вставить в свой список. Я понятия не имею, как это можно решить или что проблема в точности, но если кто-нибудь знает, как правильно получить каждую строку в файле в списке, я был бы признателен.

Спасибо заранее!

Ответ 1

В строке, где происходит ошибка, Haskell ожидает "IO a" , но вы даете ему []. Упрощая многое, на блоке do на монаде IO, каждая строка либо:

  • Что-то, возвращающее значение типа "IO a" ; значение типа "a" в нем отбрасывается (поэтому "a" часто "()" )
  • A < - выражение, которое делает то же самое, но вместо того, чтобы отбрасывать значение типа "a" , дает ему имя слева от < -
  • Позволяет, что не более чем дает имя значению

В этом блоке "hGetLine inh" возвращает строку "IO String", а String внутри нее извлекается и получает имя inpStr. Следующая строка, так как у нее ни let, ни a < - не должно быть типа "IO a" , чего нет (что вызывает ошибку компилятора). Что вы можете сделать вместо этого, поскольку у вас уже есть String, есть let:

let list' = inpStr:list

Создает новый список, состоящий из строки, за которой следует исходный список, и дает ему имя "list" .

Измените следующую строку, чтобы использовать "list" вместо "list" (таким образом, передавая ему новый список). Эта строка вызывает (рекурсивно) mainloop, который будет читать еще одну строку, называть себя и т.д. После прочтения всего файла он вернет что-то с типом "IO()". Этот "IO()" будет возвращен блоку do в loadNums. Поздравляем, вы только что создали список с строками, считанными из файла, в обратном порядке (с тех пор, как вы добавили в начало списка), а затем ничего не сделали для него.

Если вы хотите что-то сделать, измените "return()" на "return list"; return будет генерировать значение типа "IO [String]", со списком внутри него (return делает не что иное, как инкапсуляцию значения), которое вы можете извлечь при загрузкеNums с помощью < - синтаксиса.

Остальное остается в качестве упражнения для читателя.

Ответ 2

Если это не для домашней работы или чего-то еще, нет причин использовать столько усилий. Повторное использование - ленивый!

getLines = liftM lines . readFile

main = do
    list <- getLines "dictionary.txt"
    mapM_ putStrLn list

Но поскольку вы, кажется, все еще изучаете Haskell, вам важно понять, что написал CesarB.