MaybeT/Maybe and IO: Отказоустойчивое чтение информации

Я пытаюсь прочитать информацию, введенную пользователем, и проанализировать ее в тип Person, который использует тип Gender. Для этого я использую этот код:

data Person = Person String Int Gender String
data Gender = Male | Female | NotSpecified deriving Read
instance Show Gender where
 show Male = "male"
 show Female = "female"
 show NotSpecified = "not specified"
instance Show Person where
 show (Person n a g j) = "Person {name: " ++ n ++ ", age: " ++ show a ++ 
 ", gender: " ++ show g ++ ", job: " ++ j ++ "}"
readPersonMaybeT :: MaybeT IO ()
readPersonMaybeT = do
 putStrLn "Name?:"
 name <- getLine
 putStrLn "Age?:"
 ageStr <- getLine
 putStrLn "Gender?:"
 genderStr <- getLine
 putStrLn "Job?:"
 job <- getLine
 let newPerson = Person name (read ageStr) (read genderStr) job
 putStrLn $ show newPerson

Теперь я хотел бы сделать это более надежным - для этого я попытался использовать монашку MaybeT. используя этот код, я получил этот код:

readPersonMaybeT :: MaybeT IO ()
readPersonMaybeT = do
 lift $ putStrLn "Name?:"
 name <- lift getLine
 lift $ putStrLn "Age?:"
 ageStr <- lift getLine
 lift $ putStrLn "Gender?:"
 genderStr <- lift getLine
 lift $ putStrLn "Job?:"
 job <- lift getLine
 let newPerson = Person name (read ageStr) (read genderStr) job
 lift $ putStrLn "show newPerson"

Он получает компиляцию/загрузку GHCI, но когда я пытаюсь выполнить функцию readPersonMaybeT, я получаю сообщение об ошибке

Нет экземпляра для (Data.Functor.Classes.Show1 IO)   возникающие из-за использования `print ' В строке интерактивной команды GHCi: напечатайте ее

Как я могу решить эту проблему? Написав этот код, я использовал wikibook о Monad Transformers.

EDIT: Когда я пытаюсь "запустить" его с помощью runMaybeT, он запускается, но он вообще не работает безотказно. Ввод бессмыслицы для возраста, например, все еще приводит к выходу вроде

Person {name: 85, age: *** Исключение: Prelude.read: no parse.

1 ответ

Если вы выполняете проверку только после того, как вы попросили все данные, я просто использовал бы монаду IO и вернул бы Maybe:

import Text.Read
import Control.Monad.Trans.Maybe
import Control.Monad.IO.Class
askPerson :: IO (Maybe Person)
askPerson = do
 name <- putStr "Name? " >> getLine
 a <- putStr "Age? " >> getLine
 g <- putStr "Gender? " >> getLine
 return $ do age <- readMaybe a
 gender <- readMaybe g
 return $ Person name age gender

Обратите внимание, как мы используем монаду Maybe в выражении return.

Я бы использовал MaybeT, если вы хотите отказаться от запроса ввода, как только они введут недопустимое значение -

askPersonT :: MaybeT IO Person
askPersonT = do
 name <- liftIO $ putStr "Name? " >> getLine
 age <- MaybeT $ fmap readMaybe $ putStr "Age? " >> getLine
 gender <- MaybeT $ fmap readMaybe $ putStr "Gender? " >> getLine
 return $ Person name age gender
doit = runMaybeT askPersonT

Если пользователь вводит недопустимый возраст, им не будет задан пол.

licensed under cc by-sa 3.0 with attribution.