MaybeT/Maybe and IO: Failsafe reading of information - haskell

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.

Related

Let block gives indentation error

I know what an indentation error is, but I have no idea why I'm getting this error here, while every is aligned, trying to solve it for 2 hours.
Account.hs:40:25: error:
parse error (possibly incorrect indentation or mismatched brackets)
|
40 | let amount = readLn :: IO Int
| ^
Failed, 0 modules loaded.
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
I also tried this version but this also gives incorrect indentation or mismatched brackets:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
The problem is here:
-- |<---- "column 0" of this 'do' block
then do putStrLn $ "Enter your name "
-- | still good; a 'let' statement:
let name = getLine
-- |<---- "column 0" of this 'let' block
putStrLn $ "Enter the initial amount"
-- | Huh, there's no '=' in ^this^ declaration?
let amount = readLn :: IO Int
-- ^^^ Why is there a 'let' within another let binding?
-- I still haven't seen a '='. Better throw a parse error.
Basically, putStrLn $ "Enter the initial amount" is aligned with name = ... in the preceding line, so the compiler reads it as a declaration (part of the same let block).
To fix your indentation errors, it should be:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
But then you'll run into type errors:
read g is wrong: read takes a String, but g :: IO String
g == 1 is wrong: 1 is an Int, but g :: IO String
show value is wrong: show returns a String, but you're using it as an IO action
You haven't shown the declaration of Account, but you're likely going to have issues with name and amount, too
You probably want something like:
main = do
putStrLn $ "Press one to create a new account"
g <- getLine
let enteredValue = read g :: Int
if enteredValue == 1
then do putStrLn $ "Enter your name "
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
putStrLn (show value)
else do putStrLn $ "Nothing"
Basically, use v <- expr to go from expr :: IO Something to v :: Something.
Other notes:
g <- getLine; let enteredValue = read g :: Int better written as enteredValue <- readLn :: IO Int
putStrLn (show value) can be shortened to print value
you don't need do for a single expression (nor $ for a single operand): ... else putStrLn "Nothing"
There is more wrong to your code than just the Indentation Errors - so my first suggestion would be reading a bit of learn you a haskell for great good.
Next there are two assignment operators in haskell - one binds the result of an action … <- … and the other one is a local definition/declaration of a pure computation let … = ….
Moreover you can improve your reading a value by taking account of the possible false input, that someone could give you (intentionally and unintentionally) by replacing read with readMaybe, where the latter returns a Maybe something, for example readMaybe "1" = Just 1 :: Maybe Int or readMaybe "foo" = Nothing :: Maybe Int.
Regarding your indentation it is best that you compare one solution to your program with yours own:
import Text.Read (readMaybe)
data Type = Saving | Checking
deriving (Show)
data Account = Account (String,Int,Int) Type
deriving (Show)
main :: IO ()
main = do
putStrLn "Press one to create a new account"
g <- getLine
let enteredValue = readMaybe g :: Maybe Int
here the result of getLine and entered value have the same scope so they have the same indentation - we only change the scope after the next if where the then-block - and the else-block do not share the 'declarations' of each branch, so you couldn't use name in the else-block, but enteredValue can be used in both.
if enteredValue == Just 1
then do putStrLn "Enter your name "
name <- getLine
putStrLn "Enter the initial amount"
amount' <- fmap readMaybe getLine
here again name and amount' share the same scope, and pattern matching on amount' creates a new scope where amount is visible and the match on Nothing where you cannot use this variable.
case amount' of
Just amount -> print $ Account (name,1,amount) Saving
Nothing -> putStrLn "Nothing"
else putStrLn "Nothing"
let is for binding values, which is done in the form let x = y+z, where x is the name (aka "identifier") being bound, and y+z is the expression to which it is being bound.
In your example, I see three bindings: name, amount, and value. The rest are not value bindings, but actions.
In the do notation, actions do not need a let. You just write them one after another. So:
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
let value = Account (name,1,amount) Saving
show value
But wait! This is not all!
getLine is not actually an expression of type String, as you seem to be hoping here. Rather, getLine is an action. In order to get it to "run" and "produce" a String value, you need to use the <- construct instead of let:
name <- getLine
Similarly with readLn:
amount <- readLn :: IO Int
Finally, show value is not actually an action that would print the value to the screen. show is a function that takes a value and return a String. It doesn't "do" anything (i.e. doesn't produce any outside effects), so you can't use it in place of an action in the do notation. If you wanted an action that would print a value to the screen, that would be print:
print value
Gathering everything together:
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
print value
And after fixing all of that, you'll have similar difficulties with the first part of your program, where you have let g = getLine instead of g <- getLine.

Loop with the StateT monad

This is an exercise to learn the StateT monad. The program implements the game Morra. The two players are the computer and a person. The state accumulates the score of the computer and player. The program works for one iteration of function morra. However I am at a loss how to loop it. I have tried a few things but nothing seems to work.
module Morra where
import Control.Monad.Trans.State.Lazy
import Control.Monad.IO.Class
import Data.Char (isDigit, digitToInt)
import System.Random (randomRIO)
import Control.Monad (when)
morra :: StateT (Int, Int) IO ()
morra = do
p <- liftIO getChar
when (isDigit p) $
do
let p' = digitToInt p
c <- liftIO $ randomRIO (1, 2)
liftIO $ putStrLn ['P',':',' ',p] --"P: " ++ p)
liftIO $ putStrLn ("C: " ++ show c)
(pt, ct) <- get
if even (c + p') then
do
liftIO $ putStrLn "Computer Wins"
put (pt, ct + 1)
else
do
liftIO $ putStrLn "Player Wins"
put (pt + 1, ct)
main :: IO ()
main = do
putStrLn "-- p is Player"
putStrLn "-- c is Computer"
putStrLn "-- Player is odds, Computer is evens."
fScore <- runStateT morra (0,0)
let personS = fst . snd $ fScore
compS = snd . snd $ fScore
putStrLn ("Person Score: " ++ show personS)
putStrLn ("Computer Score: " ++ show compS)
if personS > compS then
putStrLn "Winner is Person"
else
putStrLn "Winner is Computer"
You're 99% there. Just add main on a new line right after the last putStrLn, and main will call itself, effectively restarting the program.
A few tricks to simplify some things in your code:
Use execStateT:: StateT s m a -> s -> m s to take just the final state of the round. This way, you don't need to use the let bindings to extract the score, and can do it inline instead: (personS,compS) <- execStateT morra (0,0)
['P',':',' ',p] can be written as ("P: " ++ [p])
It's a matter of style and preference, but you can reduce a lot of the indentation and formatting whitespace by rearranging your ifs, elses and dos:
if condition
then do
doSomethingA
doSomethingB
else someFunction $ do
doSomethingElseA
doSomethingElseB
Overall, nice job :)
I replaced p <- liftIO getChar with p <- liftIO getLine and made a few other minor changes to allow for the fact that p is now a String rather than a Char. Now it works. Seems that it has something to do with Windows as it works using getChar on linux. This is the final code:
module Morra where
import Control.Monad.Trans.State.Lazy
import Control.Monad.IO.Class
import Data.Char (isDigit, digitToInt)
import System.Random (randomRIO)
import Control.Monad (when)
morra :: StateT (Int, Int) IO ()
morra = do
p <- liftIO getLine
let p1 = head p
when (isDigit p1) $ do
let p' = digitToInt p1
c <- liftIO $ randomRIO (1, 2)
liftIO $ putStrLn ("P: " ++ p)
liftIO $ putStrLn ("C: " ++ show c)
(pt, ct) <- get
if even (c + p') then do
liftIO $ putStrLn "Computer Wins"
put (pt, ct + 1)
else do
liftIO $ putStrLn "Player Wins"
put (pt + 1, ct)
morra
main :: IO ()
main = do
putStrLn "-- p is Player"
putStrLn "-- c is Computer"
putStrLn "-- Player is odds, Computer is evens."
(personS,compS) <- execStateT morra (0,0)
putStrLn ("Person Score: " ++ show personS)
putStrLn ("Computer Score: " ++ show compS)
if personS == compS then
putStrLn "No Winner"
else if personS > compS then
putStrLn "Winner is Person"
else
putStrLn "Winner is Computer"

Parse binary tree from file in Haskell

I have task to write Quiz Animal game. I have to read Tree data from file (the structure of file is free). After end of game is possible to be added new data in tree(actually (Animal) to be replace by (Node (Animal) (Animal)).
My problem is that i can't parse content of "database.txt" in AnimalsTree
Example "database.txt" (this content is generated with writing before call 'New Game'):
Question "Is it fly?" (Animal "Bird") (Question "Is it swim?" (Animal "Fish") (Animal "Dog"))
EDIT
This is full code:
module Main where
import System.IO
data AnimalsTree = Animal String | Question String AnimalsTree AnimalsTree deriving (Read, Show)
-- Видовете отговори на потребителя
data Answer = Yes | No
main :: IO ()
main = do root <- "database.txt"
play (read root)
return ()
play :: AnimalsTree -> IO AnimalsTree
play root = do putStrLn "Think of an animal, I will try to guess what it is..."
newRoot <- play' root
writeFile "database.txt" (show newRoot)
playAgain <- ask "Do you want to play again?"
case playAgain of
Yes -> play newRoot
No -> do putStrLn "Bye!"
return newRoot
play' :: AnimalsTree -> IO AnimalsTree
play' question#(Question q l r) = do ans <- ask q
case ans of
Yes -> do y <- play' l
return $ Question q y r
No -> do n <- play' r
return $ Question q l n
play' animal#(Animal _) = do ans <- ask $ "Are you thinking of " ++ show' animal ++ "?"
case ans of
Yes -> do putStrLn "I win! :)"
return animal
No -> do putStrLn "I give up, you win!"
getNewAnimal animal
getNewAnimal :: AnimalsTree -> IO AnimalsTree
getNewAnimal animal = do putStrLn "Please help me!"
putStrLn "What is name of yout animal?"
name <- getLine
let newAnimal = Animal name
putStrLn $ "Now please enter a question that answers yes for " ++ show' newAnimal ++ " and no for " ++ show' animal
question <- getLine
return $ Question question newAnimal animal
ask :: String -> IO Answer
ask s = do putStrLn $ s ++ " (Yes/No)"
getAnswer
getAnswer :: IO Answer
getAnswer = do ans <- getLine
putStrLn ""
case ans of
"y" -> return Yes
"Y" -> return Yes
"yes" -> return Yes
"Yes" -> return Yes
"n" -> return No
"N" -> return No
"no" -> return No
"No" -> return No
_ -> putStrLn "This is incorect answer! Please try again with value in 'Yes' or 'No'!" >> getAnswer
show' (Animal name) = (if elem (head name) "AEIOUaeiou" then "an " else "a ") ++ name
show' (Question q _ _) = q
But I get following error:
test3.hs:10:19:
No instance for (Read (IO AnimalsTree))
arising from a use of ‘read’
In a stmt of a 'do' block: root <- read "database.txt"
In the expression:
do { root <- read "database.txt";
play (root);
return () }
In an equation for ‘main’:
main
= do { root <- read "database.txt";
play (root);
return () }
This looks like a great start! I only needed to add readFile to your main function to get it to compile:
main = do root <- readFile "database.txt"
It is possible that you may need to switch from readFile to explicitly opening the file for reading, doing the reading, and closing the file. This is because readFile does not guarantee when it will close the file, and writeFile is allowed to fail if it is given a path to a file that's already open.
You can read the documentation for openFile, hGetLine, and hClose for further details on how to do this (but beware hGetContents, as it has the same caveats readFile does).

Can I be sure of order of IO actions in this example?

At the moment, I have this code in and around main:
import Control.Monad
import Control.Applicative
binSearch :: Ord a => [a] -> a -> Maybe Int
main = do
xs <- lines <$> readFile "Cars1.txt"
x <- getLine <* putStr "Registration: " -- Right?
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
My hope is for “Registration: ” to be printed, then for the program to wait for the input to x. Does what I've written imply that that will be the case? Do I need the <*, or will putting the putStr expression on the line above make things work as well?
PS: I know I have to convert binSearch to work with arrays rather than lists (otherwise it's probably not worth doing a binary search), but that's a problem for another day.
The line
x <- getLine <* putStr "Registration: "
orders the IO actions left-to-right: first a line is taken as input, then the message is printed, and finally variable x is bound to the result of getLine.
Do I need the <*, or will putting the putStr expression on the line
above make things work as well?
If you want the message to precede the input, you have to put the putStr on the line above, as follows:
main :: IO ()
main = do
xs <- lines <$> readFile "Cars1.txt"
putStr "Registration: "
x <- getLine
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
Alternatively,
x <- putStr "Registration: " *> getLine
or
x <- putStr "Registration: " >> getLine
would work, but they are less readable.
Finally, since you added the lazy-evaluation tag, let me add that your question is actually not about laziness, but about how the operator <* is defined, and in particular about the order in which it sequences the IO actions.

The last statement in a 'do' construct must be an expression Haskell

Based on other similar questions I found I figure my problem has to do with indentation, but I've messed with it a lot and still can't figure it out.
addBook = do
putStrLn "Enter the title of the Book"
tit <- getLine
putStrLn "Enter the author of "++tit
aut <- getLine
putStrLn "Enter the year "++tit++" was published"
yr <- getLine
In your case it's not indentation; you really have finished your function with something that is not an expression. yr <- getLine — what did you expect to happen to yr, or for that matter aut, after this? They're just dangling, unused.
It may be clearer to show how this translates:
addBook = putStrLn "Enter the title of the Book" >>
getLine >>= \tit ->
putStrLn "Enter the author of "++ tit >>
getLine >>= \aut ->
putStrLn "Enter the year "++tit++" was published" >>
getLine >>= \yr ->
So, what did you want to have following that last arrow?
Think about the type of addBook. It's IO a where a is... nothing. That doesn't work. Your monad must have some result.
You might want to add something like this at the end:
return (tit, aut, yr)
Alternatively, if you don't want to have any useful result, return an empty tuple (a unit):
return ()
If you take your code:
addBook = do
putStrLn "Enter the title of the Book"
tit <- getLine
putStrLn "Enter the author of "++tit
aut <- getLine
putStrLn "Enter the year "++tit++" was published"
yr <- getLine
and "translate" it to "normal" (non-do) notation (given p = putStrLn "..."):
addBook =
p >> getLine >>= (\tit ->
p >> getLine >>= (\aut ->
p >> getLine >>= (yr ->
You are ending up with (yr -> that doesn't make sense. If you don't have anything else useful to do, you can just return an empty tuple:
return ()
at the end:
addBook = do
putStrLn "Enter the title of the Book"
tit <- getLine
putStrLn "Enter the author of "++tit
aut <- getLine
putStrLn "Enter the year "++tit++" was published"
yr <- getLine
return ()
You should probably ask yourself why you need to get aut and yr though.
remove the last line since it's not an expression,
then use parenthesis for the strings you pass to putStrLn.

Resources