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?<
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.