В строке, где происходит ошибка, Haskell ожидает «IO a», но вы даете ему []. Многое упрощая, в блоке do монады ввода-вывода каждая строка имеет вид:
- Что-то, что возвращает значение типа "IO a"; значение типа "a" внутри него отбрасывается (поэтому часто "a" равно "()")
- A <- выражение, которое делает то же самое, но вместо отбрасывания значения типа «a» присваивает ему имя слева от <- </li>
- let, которая ничего не делает, кроме как присваивает имя значению
В этом блоке do «hGetLine inh» возвращает «IO String», и строка в нем извлекается и получает имя inpStr. Следующая строка, поскольку это ни let, ни a <-, должна иметь тип "IO a", чего нет (что приводит к ошибке компилятора). Вместо этого вы можете сделать следующее, поскольку у вас уже есть строка: </p>
let list' = inpStr:list
Это создает новый список, состоящий из String, за которым следует исходный список, и дает ему имя «list».
Измените следующую строку, чтобы использовать «список» вместо «список» (таким образом передавая ему новый список). Эта строка вызывает (рекурсивно) mainloop, который будет читать еще одну строку, вызывать себя и так далее. После прочтения всего файла он вернет что-то с типом «IO ()». Этот «IO ()» будет возвращен блоку do в loadNums. Поздравляем, вы только что создали список со строками, прочитанными из файла, в обратном порядке (так как вы добавляли заголовок списка), а затем ничего не сделали с ним.
Если вы хотите что-то с этим сделать, измените return () на return list; return генерирует значение типа "IO [String]" со списком внутри него (return не делает ничего, кроме инкапсуляции значения), которое вы можете извлечь в loadNums с синтаксисом <-. </p>
Остальное оставлено читателю в качестве упражнения.