I am trying to read information entered by the user and to parse it into the type Person
, which uses the type Gender
. To do so, I use this code:
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
Now I would like to make this more failsafe - to achieve this I tried to use the MaybeT monad. using this, I got this code:
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"
It get compiles/loaded by the GHCI, but when I try to execute the readPersonMaybeT
function I get the error-message
No instance for (Data.Functor.Classes.Show1 IO) arising from a use of `print' In a stmt of an interactive GHCi command: print it
How can I solve this issue? Writing this code, I used the wikibook about Monad Transformers.
EDIT: When I try to 'run' it with runMaybeT
it gets executed, but it is not failsafe at all. Entering nonsense for the age for example still results in a output like
Person {name: 85, age: *** Exception: Prelude.read: no parse.
If you are doing the validation only after you have asked for all of the input, I would just use the IO monad and return a 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
Note how we are using the Maybe monad in the return
statement.
I would use MaybeT if you want to quit asking for input once they enter an invalid value --
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
If the user enters an invalid age they won't be asked for a gender.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With