Why "Empty do" error when my do isn't empty? - haskell

I am trying to create a menu which gives output based on user input. However, I get a empty do error even though I have code for it to do underneath it. Am I missing something?
main :: IO()
main = do
contents <- readFile "spa.txt"
let storage = (read contents :: [Spa])
putStrLn "Please Enter Your Name: "
name <- getLine
putStrLn ""
putStrLn ("Welcome " ++ name)
menu storage
putStrLn ""
where menu resDB = do
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
putStrLn "2: Exit"
putStr "\nSelected option: "
putStrLn ""
option <- getLine
output :: Int -> IO ()
output = do
case option of
1 -> putStrLn "Enter Spa ID: "

This is indeed an indentation problem. Let me just give a version that parses correctly and is eye-friendly:
main :: IO ()
main = do
contents <- readFile "spa.txt"
let storage = read contents :: [Spa]
-- ...
menu storage
where menu resDB = do
putStrLn "~~~"
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
-- ...
option <- getLine
putStrLn "~~~"
output option
output :: Int -> IO ()
output option = case option of
1 -> putStrLn "Enter Spa ID: "
Note that output is indented only to the level of the where block, not the do block. Generally, do blocks are for writing statements (monadic actions), not for giving declarations like you tried here. You can always embed declarations in a do block, but you need to put them in a let block: this also works, and allows omitting option as an explicit argument to output because they're now inside the same local scope:
where menu resDB = do
putStrLn "~~~"
option <- getLine
let output :: IO ()
output = case option of
1 -> putStrLn "Enter Spa ID: "
output
But, if you're only defining output in order to immediately invoke it exactly once, then you might as well inline the declaration entirely:
where menu resDB = do
putStrLn "~~~"
option <- getLine
case option of
1 -> putStrLn "Enter Spa ID: "
Depending on the amount of code, a named declaration does make sense though.
You can reduce the needed indentation even more: this style avoids the seven-space indented where block. I personally don't like it as much though.
main :: IO ()
main = do
contents <- readFile "spa.txt"
let storage = read contents :: [Spa]
-- ...
menu storage
where
menu resDB = do
putStrLn "~~~"
-- ...
And both menu and output could also be declared at the top-level (i.e. with no indentation at all), provided that you do use explicit arguments to pass around the data. Furthermore, you can use just different clauses for that case distinction in output:
main :: IO ()
main = do
contents <- readFile "spa.txt"
menu $ read storage
menu :: [Spa] -> IO ()
menu resDB = do
putStrLn "~~~"
-- ...
option <- getLine
output option
output :: Int -> IO ()
output 1 = putStrLn "Enter Spa ID: "
output 2 = ...

Try this:
main :: IO()
main = do
contents <- readFile "spa.txt"
let storage = (read contents :: [Spa])
putStrLn "Please Enter Your Name: "
name <- getLine
putStrLn ""
putStrLn ("Welcome " ++ name)
menu storage
putStrLn "" where
menu resDB = do
putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
putStrLn "2: Show all spas in the database"
putStrLn "3: Give all spas operating in a certain area"
putStrLn "4: Give all spas that have a performance of 8 or higher "
putStrLn "5: Give the average performance for the spas in a certain area "
putStrLn "6: Give the names of the spas a given supervisor has rated the service level, along with that rating result for each spa."
putStrLn "7: Give the names of the spas a given supervisor has yet to rate the service level, along with that spa performance."
putStrLn "8: Allow a given chef rating to be entered (or updated) for a restaurant he has rated (note that only the latest result from the supervsior should remain recorded)"
putStrLn "9: Exit"
putStr "\nSelected option: "
putStrLn ""
option <- getLine
putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
output :: Int -> IO ()
output option = do
case option of
1 -> putStrLn "Enter Spa ID: "

The statements in a do block must be indented further than the start of the line containing the do. But you have other problems too, like using a let, which does not make sense here.

Related

Getting putStrLn to work inside an if-else block

I am writing a palindrome solution in Haskell, and I want the function to show an error if a null is entered. I do not want to use the error function as that halts the program. Hence, I want to show an error message using putStrLn and continue the loop.
I have tried using show to change the input given to the putStrLn but it doesn't work and throws compile time type-error.
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then
-- putStrLn "This is not a word!"
main
else do
putStrLn $ show $ checkPalindrome word
main
checkPalindrome w = if reverse w == w then True else False
I expect it to show an error, but it only gives an error. What are possible solutions to show a halt-safe error?
If you write both a putStrLn "this is not a word!" and a main, you should use a do block here:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then do
putStrLn "This is not a word!"
main
else do
putStrLn $ show $ checkPalindrome word
main
That being said, you can simplify the above by making a call at the bottom of the do block of the main:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then putStrLn "This is not a word!"
else putStrLn $ show $ checkPalindrome word
main
or we can, like #Bergi says, even put more in the main block, like:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
putStrLn $ if null word
then "This is not a word!"
else show $ checkPalindrome word
main
If you write this without do block, Haskell aims to parse putStrLn "This is not a word!" main. This thus means that putStrLn is supposed to have type String -> IO a -> IO a, but that is not the case.
By using a do block, Haskell will desugar the do block [Haskell'10 report] into putStrLn "This is not a word!" >> main, and this is sound (at least for the type system). Since the bind operator has type (>>) :: Monad m => m a -> m b -> m b.

How can I bind two IO () monads without executing them?

In the below code, I am using >> to concatenate IO actions together. But AFAIU, m1>>m2 gets de-sugared to m1>>=(\_.m2) and thus it is executing the first IO action right when it is binding. I want all printing to happen in the main, i.e. print statements should not get interleaved with the input statements ("Enter Code"). Since do doesn't allow me to return any other monad than IO like [IO ()]. How can I have the desired printing effect?
f :: [Int] -> IO ()
f inventory = do
putStrLn "Enter Code\n"
x <- getLine
let idx = nameToIndex x
putStrLn "Quantity\n"
y <- getLine
putStrLn "More?\n"
c <- getChar
let q = (read y :: Int)
let curM = if inventory !! idx >= q then (putStrLn "sdaf\n") else (putStrLn "Overflow!\n")
if c == 'Y' then curM>>(f (update inventory idx)) else curM
main = f [1, 2]
I'm not 100% sure I understand the problem, but I think it goes like this: you'd like to do some interactions with the user, storing up information about the interaction, then display all the information at once at the end of the entire interaction.
Here's a very simplified version of your code, that skips all the business logic and just keeps asking the user if they want to continue.
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> putStrLn "Okay, let's continue." >> prompt
_ -> return ()
main = prompt
I think the effect you're asking for is to delay the display of "Okay, let's continue." until the user has stopped hitting "y". That's no problem. There's lots of ways you can do this. The most flexible is to have prompt return the action it wants to be executed after it completes:
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> do
act <- prompt
return (putStrLn "Okay, let's continue." >> act)
_ -> return (return ())
main = do
act <- prompt
act
(There are combinators that can make this code more compact, as well.) But I don't like this design; it makes it difficult to introspect on the result of prompt. A more specialized but also more maintainable approach is to return some data describing the interaction, which the caller can then turn into an IO action summarizing things. In this case, a list of strings seems like a suitable description.
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> do
results <- prompt
return ("Okay, let's continue." : results)
_ -> return []
main = do
results <- prompt
mapM_ putStrLn results
Hopefully this explanation is clear enough that you can combine this idea with your more complicated business logic.

Trying to deal with IO actions

I'm trying to deal with IO actions. I wonder why does this work:
main = do
alias = getLine
name <- alias
putStrLn ("your name is: " ++ name)
saying
parse error on input `='
Add keyword let
main = do
let alias = getLine
name <- alias
putStrLn ("your name is: " ++ name)
do is a specified construction for monadic bind operator, it's not a cosmic space. All you write into block do is really chain of >>= monaidic functions. So you should use let construction.
But you can make alias in other part of you program.
alias = getLine
main = do
name <- alias
putStrLn ("your name is: " ++ name)

Print Sum of two numbers Haskell

How can I print the result of sum of two numbers?
main:: IO()
main = do putStrLn "Insert the first value: "
one <- getLine
putStrLn "Insert the second value: "
two <- getLine
putStrLn "The result is:"
print (one+two)
This gives me an error:
ERROR file:.\IO.hs:3 - Type error in application
*** Expression : putStrLn "The result is:" print (one + two)
*** Term : putStrLn
*** Type : String -> IO ()
*** Does not match : a -> b -> c -> d
Try to use readLn instead of getLine.
getLine returns a String in the IO monad and Strings cannot be added.
readLn has polymorphic return type, and the compiler infers that the return type is Integer (in the IO monad) so you can add them.
I'm going to take a guess that your error is related to not using parens.
Also, since getLine produces a string, you'll need to convert it to the correct type. We can use read to get a number from it, although it's possible it will cause an error if the string cannot be parsed, so you might want to check it only contains numbers before reading.
print (read one + read two)
Depending on precedence, the variables may be parsed to belong as parameters for print instead of to +. By using parens, we ensure the variables are associated with + and only the result of that is for print.
Lastly, make sure the indentation is correct. The way you've pasted it here is not correct with the do-expression. The first putStrLn should be on the same indentation level as the rest - at least ghc complains about it.
You can modify your code this way using the read :: Read a => String -> a
main:: IO()
main = do putStrLn "Insert the first value: "
one <- getLine
putStrLn "Insert the second value: "
two <- getLine
putStrLn "The result is:"
print ((read one) + (read two))

Type error in Haskell program

User can give id, width, height and description rectangle and then I write it to a file. Now I would like to load this content from the file to my program but I have error:
Couldn't match expected type [RectangleType] against inferred type IO [Rectangletype]. In the first argument of menuRectangles namely db. In the expression menuRectangles db. In a do expression menuRectangles db.
What is going on ? This is content of my file:
[Rectangle 2 5 6 "abcabc",Rectangle 1 2 4 "abcabc"]
This is code:
import IO
import Char
import System.Exit
import Maybe
data RectangleType = Rectangle Int Int Int deriving(Show, Read)
loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
catch (do fileContent <- readFile fname
return (read fileContent)
) errorHandler
where
errorHandler e = do putStrLn ("Error file")
exitFailure
db = loadFile "db.txt"
main = do
putStrLn "Choose option:"
n <- getLine
case n of
"1" -> do menuRectangles db; main
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; main
menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
putStrLn "Please choose option:"
putStrLn "1 - Add rectangle"
putStrLn "2 - Show rectangle"
putStrLn "3 - Quit"
putStr "Number: "
n <- getLine
case n of
"1" -> do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
"2" -> do { {- showRectangle rs; -} menuRectangles rs }
"3" -> do { putStrLn "Quitting"; return rs }
otherwise -> do { putStrLn "The End"; return rs }
EDIT:
correct code:
import IO
import Char
import System.Exit
import Maybe
data RectangleType = Rectangle Int Int Int deriving(Show, Read)
loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
catch (do fileContent <- readFile fname
return (read fileContent)
) errorHandler
where
errorHandler e = do putStrLn ("Error file")
exitFailure
main = do
db <- loadFile "db.txt"
mainMenu db
mainMenu rs = do
putStrLn "Choose option:"
n <- getLine
case n of
"1" -> do menuRectangles rs; mainMenu rs
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; mainMenu rs
menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
putStrLn "Please choose option:"
putStrLn "1 - Add rectangle"
putStrLn "2 - Show rectangle"
putStrLn "3 - Quit"
putStr "Number: "
n <- getLine
case n of
"1" -> do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
"2" -> do { {- showRectangle rs; -} menuRectangles rs }
"3" -> do { putStrLn "Quitting"; return rs }
otherwise -> do { putStrLn "The End"; return rs }
In Haskell there exists a concept called pure code. Pure code does not have any of the following: user-input values, system calls, pseudorandom number generation, calls to non-pure code, etc.
Pure functions are guaranteed to always always always have the same behavior based on the lexical content of your program (e.g. they can return different values, but the reason they return different values cannot depend on "the world").
This is a very powerful policy in functional languages, and allows very powerful code. For example, you know that calling a function won't change some unrelated global variable or the state of some other data structure. I often apply this policy to python code.
The way Haskell enforces the purity-impurity thing is with the IO monad.
Any of things that "the world" touches are wrapped up in the IO monad, which represented that the values have been "tainted". If anything touches these "tainted" values, the values they return must also be tainted.
You will need to run your pure code within the IO monad if you want it to have access to those values. Once within the IO monad, you can "unwrap" the value you've read and pass it into your pure code, then your pure code returns value, then you can print your value. Everything works as expected, but you have to do it in the IO monad.
Note that it is good form to ensure that most of your code is written in a pure, functional form outide of the IO monad. e.g. a pure doStuffWithRectangles function, that is then called from within the IO monad.
The beautiful thing about it is that your pure code doesn't need to know the Rectangle value has been tainted by being of type IO Rectangle. As long as you work in the IO monad, your pure code will think it is just a normal Rectangle.
And just to make this answer more explicit: readFile returns things wrapped in the IO monad, because those things come from "the world" (the filesystem) and for example if you changed the file during program execution, it might return a different value.
--db = loadFile "db.txt" REMOVED--
main = do --within the IO monad
putStrLn "Choose option:"
n <- getLine
**DB <- LOADFILE "db.txt"** --db is now a pure value
case n of
"1" -> do menuRectangles db; main
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; main
More information: http://www.haskell.org/tutorial/io.html
A good online/offile read: http://learnyouahaskell.com/
Also a good online/offile read: http://book.realworldhaskell.org/

Resources