1

I am trying to create a menu which read the data from a textfile. But I get three errors that variable not in scope despite having at the start of the IO(). Am I not reading the txt.file properly and is the way I creating the menu wrong?

errors

Variable not in scope: spaDatabase :: [Spa]

--line of error for spaDatabase
let updatedDB = (addSpa rid br ar (read st) spaDatabase)

Variable not in scope: spaDB
Variable not in scope: updatedDB :: [Spa]

--line of error for updatedDB
2 -> putStrLn (spaListStr updatedDB) >> menu spaDB

My code

main :: IO()
main = do 
       contents <- readFile "spa.txt"
       let spaDatabase = (read contents :: [Spa])
       menu spaDatabase
       putStrLn "" 
     where  menu spaDatabase = do
                putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" 
                putStrLn "\nPlease select an option:"
                putStrLn "1: Add a new spa to the database "       
                putStrLn "2: Show all spa "   
                putStr "\nSelected option: "
                putStrLn ""
                option <- getLine    
                putStrLn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
                
            output :: Int -> IO ()
            output option = do 
                case option of 1 -> do putStrLn "Enter Spa ID: "
                                       rid <- getLine
                                       putStrLn "Enter Spa Brand Name: "
                                       br <- getLine
                                       putStrLn "Enter Spa Area: "
                                       ar <- getLine
                                       putStrLn "Enter Spa Stars: "
                                       st <- getLine
                                       let updatedDB = (addSpa rid br ar (read st) spaDatabase)
                                       putStrLn (spaListStr updatedDB)
                                       writeFile "spa.txt" (spaListStr updatedDB)

                        
                               2 -> putStrLn (spaListStr updatedDB) >> menu spaDB
                    
   
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Rick Po
  • 59
  • 6
  • In the where of the `main` function you define two functions `menu` and `output`. These function do not inherite the scope of the `do` block. Now since `output` has no parameter named `spaDatabase` and no variable higher in the scope exists you get the error. – Ackdari Jul 24 '20 at 08:37
  • 5
    I would suggest to avoid `do .. where`. The variables you bind inside the `do` are not visible inside `where`. If you only use `let` inside `do` instead you don't have this issue. – chi Jul 24 '20 at 08:37
  • Thank you chi! Although now I have a parse error menu spaDatabase = do, is that due to indentation? – Rick Po Jul 24 '20 at 08:46
  • edits should not invalidate existing answers. I rolled back. If you have new question to ask with a new code, you're welcome to post a new question, stating it's a followup to this one, including the link to it if you feel it's relevant. :) – Will Ness Jul 24 '20 at 13:10
  • Thank you Will! I manage to clear the top part error but it become a parse error at the second choice. I will add the link from this one to that one. – Rick Po Jul 24 '20 at 13:33

1 Answers1

1

where is part of a definition, not of expression. It belongs to the main's definition so its scope goes above do's. Your originally posted code

main = do { .....
            let c = .....
            .....
          }
       where
          a = ....
          b = ....c....
          .....

is equivalent to

main = let {       -- where's translation
          a = ....
          b = ....c....
          .....
          }
       in
          do {
               .....
               let c = .....
               .....
             }

You're using c in b = ....c.... before it is introduced with let c = ....., i.e. outside of its scope region. That's an error, "variable not in scope".

update. let in do blocks is just a shortcut:

              do {
                   .....
                   let c = .....
                   .....
                 }

is the same as

              do {
                   .....
                   let c = .....
                   in do {
                           .....
                         }
                 }

The working code could be structured, as also @chi proposed in the comments, as

main = do { .....
            let c = .....
            .....
            let { a = ....
                  b = ....c....
                }
            .....
         }

That way a and b are in c's scope, so may use c as part of their definitions.

Will Ness
  • 70,110
  • 9
  • 98
  • 181