I'd like to keep track of a "current" value in a succession of immutable values. What is the best way to do that in Haskell without introducing a new reference for every new value? Here is an example:
data Person = Person {name, level, topic :: String }
deriving(Show)
dierk :: Person
dierk = Person "Dierk" "confident" "Java"
works :: Person -> String
works person = name person ++ " is " ++ level person ++ " in " ++ topic person
main _ = do
putStrLn $ works dierk
-- do more with "current" topic
putStrLn $ works dierk {level= "proficient", topic="Groovy"}
-- do more with "current" topic
putStrLn $ works dierk {level= "dabbling", topic="Haskell"}
-- do more with "current" topic
I'm not sure about what the question really asks for. The posted example
can be rewritten to use the StateT Person IO
monad as follows.
import Control.Monad.State
data Person = Person {name, level, topic :: String }
deriving Show
dierk :: Person
dierk = Person "Dierk" "confident" "Java"
works :: Person -> String
works person = name person ++ " is " ++ level person ++ " in " ++ topic person
main :: IO ()
main = flip evalStateT dierk $ do
-- use the current topic
lift . putStrLn . works =<< get
-- change the current topic
modify (\t -> t{level= "proficient", topic="Groovy"})
lift . putStrLn . works =<< get
-- change the current topic
modify (\t -> t{level= "dabbling", topic="Haskell"})
lift . putStrLn . works =<< get
{- Output:
Dierk is confident in Java
Dierk is proficient in Groovy
Dierk is dabbling in Haskell
-}
If instead a real reference type is wanted, one could use IORef Person
, or STRef
if in the ST
monad. But in such case, you must be working inside some monad allowing these reference types. By comparison, StateT Person m
works in any monad m
.