StrunKzZ
StrunKzZ

Reputation: 21

How do I save changes I made to a list in an immutable way?

I need to ask user for input, do something with it, ask for next input and do something with it but I still need to take the previous input into account.

I was thinking(in an OOP way) of saving all the input <- getLines into a list and then be able to make changes to that list(and store them), but I can't do that because stuff immutable in haskell.

main =  do
    args <- getArgs
    contents <- readFile $ head args
    putStrLn contents
    let inputs = []  --THIS
    forever $ do
        putStrLn $ "Filtering: " ++ appendFilters inputs
        input <- getLine
        let inputs = updateInputs inputs input --THIS
        newInput <- getUserInputs inputs input
        putStrLn $ unlines (filterContent (lines contents) newInput)

How do I do this right in the eyes of functional programming?

Edit:

So what my program is supposed to do is:

  1. Read contents from file
  2. Get user input in order to only display lines containing those words
  3. Get more user input, if input == "pop" then delete last filter, otherwise add to the list of filtering words
  4. If user tries to pop an empty list of filters, exit program

Upvotes: 0

Views: 80

Answers (1)

leftaroundabout
leftaroundabout

Reputation: 120711

First off, you can have mutable values in Haskell, you just need to make all the updates in the IO monad. We usually avoid that, but if your code is all about IO interactivity anyway then it may be the most straightforward solution.

import Data.IORef

main =  do
    args <- getArgs
    contents <- readFile $ head args
    putStrLn contents
    inputs <- newIORef []
    forever $ do
        currentInputs <- readIORef inputs
        putStrLn $ "Filtering: " ++ appendFilters currentInputs
        input <- getLine
        updateInputs inputs input
        newInput <- getUserInputs inputs input
        putStrLn $ unlines (filterContent (lines contents) newInput)

The more functional alternative is generally to explicitly pass updated versions of the stateful variables through the recursion “loops”. You won't be able to use forever then, but it's easy to change that to manual recursion:

main =  do
    args <- getArgs
    contents <- readFile $ head args
    putStrLn contents
    let loop inputs = do
         putStrLn $ "Filtering: " ++ appendFilters inputs
         input <- getLine
         newInput <- getUserInputs inputs input
         putStrLn $ unlines (filterContent (lines contents) newInput)
         loop $ updateInputs inputs input
    loop []

Upvotes: 3

Related Questions