Я делал небольшую программу, которую никогда не ставил на github и не распространял, и заметил, что в командной строке просят что-то задавать, получать ответ и использовать его. Поэтому я хотел превратить этот шаблон в функцию.
askQuestion :: (Read a) => String -> IO a
askQuestion q = do
putStrLn q
read <$> getLine
Конечно, функция такого типа не особенно безопасна, поскольку выдает ошибку, когда ответ пользователя не может быть проанализирован. , Именно поэтому readMaybe или readEither рекомендуется. Но так как я единственный пользователь кода, я не против, потому что я знаю, что нужно для моей программы.
После этого я подумал о другом шаблоне, который мой код использовал много, который просит пользователя выбрать между двумя вариантами, после чего моя программа разветвляется в зависимости от ответа. Я также сделал для этого функцию.
askOption :: String -> String -> String -> IO a -> IO a -> IO a
askOption q a1 a2 ac1 ac2 = do
putStrLn q
ans <- getLine
if a1 == ans
then ac1
else if a2 == ans
then ac2
else error $ "Not one of the options for question " ++ q
Этот код отлично работал для того, что я хотел, и мои основные функции выглядели довольно чисто. Но когда я заметил
putStrLn q
ans <- getLine
в своей функции askOption, я захотел заменить этот блок функцией askQuestion.
В этом и заключается моя проблема, очевидно, чтение не может проанализировать строку для строка. Теперь, я думаю, было совершенно очевидно, что вы не можете этого ожидать, но я надеялся, что read просто переместит строку, ничего не делая с ней.
Что я хотел бы знать, так это то, возможно ли иметь систему, в которой если read замечает, что это строка, она просто пропускает ее. Я хочу сохранить чистый тип ввода-вывода a и не выполнять рефакторинг своего кода для использования IO Maybe a или IO Either String a. Я знаю, что могу обработать ошибку, а затем в дескрипторе использовать getLine, чтобы получить ответ. Но это потребовало бы, чтобы я набирал ответ дважды каждый раз. Один раз потерпеть неудачу и один раз, когда это удастся.
ans <- handle (\(SomeException e) -> putStrLn "Answer again" >> getLine) $ askQuestion "Give Input"
Это сохранит нужный мне тип, но будет проблемой для самой программы.
Другой вариант, о котором я подумал, это проверка, является ли ans строкой в функции askQuestion, и если это было возвращение строки и выполнение чтения. Но это нарушило бы тип ввода-вывода, поскольку строка ввода-вывода не равна значению ввода-вывода.
Резюме: есть ли способ сделать так, чтобы сигнатура типа askQuestion оставалась ввода-вывода во время передачи ответов, которые не могут читать как строки. Я знаю, что использование readEither / readMaybe предпочтительнее в реальной практике, но я просто хотел сделать это для развлечения.