Why “Empty do” error when my do isn't empty?

后端 未结 3 1513
野的像风
野的像风 2021-01-29 09:38

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

相关标签:
3条回答
  • 2021-01-29 09:56

    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 = ...
    
    0 讨论(0)
  • 2021-01-29 09:58

    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: "
    
    
    0 讨论(0)
  • 2021-01-29 10:16

    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.

    0 讨论(0)
提交回复
热议问题