I want to write an Haskell program that read a list of integer positive numbers from stdin, if user write a different thing like a list of negative numbers, a list of characters or a thing that isn't a list, program need to advise user and read again from stdin until user write a correct list. This is what I wrote but if user type a list that contain a char, or a single single digit/char without close it in square brackets, program ends. Instead if user type a list containing negative numbers, or empty list, program works well. Thanks in advice.
main :: IO()
main = do
putStrLn "\nType a list of positive integers enclosed in square brackets and separated by commas:"
list <- readIntList
putStrLn "\nList:"
print list
readIntList :: IO [Double]
readIntList = do
readedList <- getLine
let list = read readedList
case checkList list && not (null list) of
True -> return list
False -> putStrLn "\nInvalid input, type again:" >> readIntList
checkList :: [Double] -> Bool
checkList = all checkNumber
checkNumber :: (Ord a, Num a) => a -> Bool
checkNumber n
| n > 0 = True
| otherwise = False
CodePudding user response:
First, let's take a look to your solution and what's wrong with it. Errors are marked with a comment.
main :: IO()
main = do
putStrLn "\nType a list of positive integers enclosed in square brackets and separated by commas:"
list <- readIntList
putStrLn "\nList:"
print list
readIntList :: IO [Double]
readIntList = do
readedList <- getLine
-- This will fail!! at run time. read function expect wellform input,
-- Therefore, you can't control the errors this way
let list = read readedList
-- This is kind of right, but it could be done better by pattern matching
case checkList list && not (null list) of
True -> return list
False -> putStrLn "\nInvalid input, type again:" >> readIntList
-- These two can be simplify into one. See below
checkList :: [Double] -> Bool
checkList = all checkNumber
checkNumber :: (Ord a, Num a) => a -> Bool
checkNumber n
| n > 0 = True
| otherwise = False
Now, we know what's wrong with your implementation. Let's fix it
import Text.Read
main :: IO()
main = do
putStrLn "\nType a list of positive integers enclosed in square brackets and separated by commas:"
list <- readIntList
putStrLn "\nList:"
print list
readIntList :: IO [Double]
readIntList = do
readedList <- getLine
-- use readMaybe from Text.Read module. It returns Nothing if the input is badform
-- returns, Just xs otherwise
case readMaybe readedList of
Nothing -> do
-- This is the case when input isn't a list of numbers
putStrLn "Invalid Input type. Try again:\n"
readIntList
Just [] -> do
-- This is the case of an empty list input.
putStrLn "Input must be not empty. Try again:\n"
readIntList
Just xs ->
-- This is the case input is a nonempty list of numbers, but we don't know if positive
-- hence we must check all positive numbers.
if checkList xs
then pure xs
else do
putStrLn "all input numbers must be positive. Try again:\n"
readIntList
-- Checking all positives numbers is as simple as this one.
checkList :: [Double] -> Bool
checkList = all (>0)
For the sake of completeness, the last pattern match can be simplify using guards, but it isn't that known syntax
case readMaybe readedList of
-- ... other patterns as before
Just xs
| checkList xs -> pure xs
| otherwise -> do
putStrLn "all input numbers must be positive. Try again:\n"
readIntList