Jack Evans
Jack Evans

Reputation: 13

Haskell: Case statement issue

I'm new to Haskell and i have to do a project for university. I'm having trouble using a case statement.

case words command of
        ("Highlight":_) -> putStrLn ("Highlight                   |              \x1b[32m\x1b[107m" ++ show (aLine) ++ "\x1b[0m")
        ("InsertChar":_) -> putStrLn ("Insert Char                 |              " ++ show (insertChar aLine))
        ("DelCharRight":_) -> putStrLn ("Delete Char Right           |              " ++ show (delCharRight aLine))
        ("DelCharLeft":_) -> putStrLn ("Delete Char Left            |              " ++ show (delCharLeft aLine)) 
        ("CharRight":_) -> putStrLn ("Move Char Right             |              " ++ show (charRight aLine))
        ("CharLeft":_) -> putStrLn ("Move Char Left              |              " ++ show (charLeft aLine))
        ("WordRight":_) -> putStrLn ("Move Word Right             |              " ++ show (wordRight aLine))
        ("WordLeft":_) -> putStrLn ("Move Word Left              |              " ++ show (wordLeft aLine))
        ("EndLineRight":_) -> putStrLn ("End Line Right              |              " ++ show (endLineRight aLine))
        ("EndLineLeft":_) -> putStrLn ("End Line Left               |              " ++ show (endLineLeft aLine))
        ("HighlightRight":_) -> putStrLn ("Highlight Right             |              " ++ show (highlightRight aLine))
        ("HighlightLeft":_) -> putStrLn ("Highlight Left              |              " ++ show (highlightLeft aLine))
        ("HighlightWordRight":_) -> putStrLn ("Highlight Word Right        |              " ++ show (highlightWordRight aLine))
        ("HighlightWordLeft":_) -> putStrLn ("Highlight Word Left         |              " ++ show (highlightWordLeft aLine))
        ("Copy":_) -> putStrLn ("Copy                        |              " ++ show (copy aLine))
        ("Cut":_) -> putStrLn ("Cut                         |              " ++ show (cut aLine))
        ("Paste":_) -> putStrLn ("Paste                       |              " ++ show (paste aLine))
        ("Destroy":_) -> putStrLn ("Destroy                     |              " ++ show (destroy aLine))
        ("ClearScreen":_) -> clearScreen
        ("Save":_) -> writeFile savePath (command ++ "           |              " ++ show (save aLine))
        _ -> putStrLn "Error: Incorrect Command"
    return command

How would i add more code to the same line of command? I want something like this:

("DelCharRight":_) -> putStrLn ("Delete Char Right           |              " ++ show (delCharRight aLine) let bLine = delCharRight aLine let aLine = bLine

This basically allows me to overwrite a Line after each command so i can keep typing the commands to edit the line of text.

Trouble is the case statement won't let me use 'Let'.

I apologise for the terrible code and explanation!

Upvotes: 1

Views: 130

Answers (2)

Probie
Probie

Reputation: 1361

To answer your exact question, you can just add do after the ->

("DelCharRight":_) -> do
    putStrLn ("Delete Char Right           |              " ++ show (delCharRight aLine) 
    let bLine = delCharRight aLine 
    let aLine = bLine

But it won't help - a let statement in Haskell can't do what you want. As a general rule, you can't mutate data in Haskell. aLine will only ever have one value. If you have a background in imperative programming, you might look at the following code and (incorrectly) deduce that some sort of mutation is going on, with x changing it's value. But all that's going on is shadowing (try let x = x + x) and see what that does.

someFunc :: IO ()
someFunc = do
    let x = 3
    let x = 5
    print x

That said, because you are in IO you do have access to mutation. IORefs (found in Data.IORef) are mutable references that you could make use of.

someFunc' :: IO ()
someFunc' = do
    x <- newIORef 3
    writeIORef x 5
    readIORef x >>= print

But I'd recommend against using an IORef. It's much more idiomatic Haskell to write a separate function which returns the changed value, and then feed the result back in as an input on the next call. Here's an example of that technique for a simple "Counter".

main :: IO ()
main = do
    commandLine <- getLine
    let commands = words commandLine
    counter <- runCounter commands 0
    print counter

-- There is a higher-order functions (foldM) that we 
-- could use instead of this function
-- but we'll avoid it for the sake of clarity
runCounter :: [String] -> Int -> IO Int
runCounter [] counter = return counter
runCounter (cmd:cmds) counter = do
    newCounter <- updateCounter cmd counter
    runCounter cmds counter

updateCounter :: String -> Int -> IO Int
updateCounter command counter = case command of
    "UP" -> do
        putStrLn "Incrementing counter"
        return (counter + 1)
    "DOWN" -> do
        putStrLn "Decrementing counter"
        return (counter - 1)

Upvotes: 1

chi
chi

Reputation: 116139

Option 1: add a do

case ... of
  ...
  ("DelCharRight":_) -> do
     let bLine = delCharRight aLine
         aLine = bLine
     putStrLn (...)

Option 2: use let .. in ..

case ... of
  ...
  ("DelCharRight":_) ->
     let bLine = delCharRight aLine
         aLine = bLine
     in putStrLn (...)

Option 2 is more general, since it works even when we are not working inside some monad (here we are inside IO, so do is also a possibility).

Upvotes: 0

Related Questions