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