Oops, i did misread the example slightly sorry ^^ You’re using the IOScopedRef to implement modifySeverity not to modify the Logger value directly.
My point still stands though. Everything that (indirectly) uses this IOScopedRef needs to go through the logger variable anyway so you might as well use it to carry the state in a lexically scoped way.
OK, then tell me how I could obtain the same behaviour using a lexically-scoped variable but without
IOScopedRef.
data Logger = Logger {
logImpl :: Severity -> String -> IO (),
baseSeverity :: Severity
}
logMsg :: Logger -> Severity -> String -> IO ()
logMsg logger severity msg = logger.logImpl (logger.baseSeverity + severity) msg
newLogger :: Severity -> Logger
newLogger baseSeverity = Logger { baseSeverity, logImpl = \severity message -> putStrLn ... }
modifySeverity :: Logger -> (Severity -> Severity) -> Logger
modifySeverity logger f = logger { baseSeverity = f logger.baseSeverity }
loggerExample :: IO ()
loggerExample = do
let logger = newLogger 0
logMsg logger 1 "Getting user"
user <- getUser
logMsg logger 1 ("Is VIP: " <> show (isVip user))
let modification = if isVip user then (+ 10) else id
d <- do
logger <- pure (modifySeverity logger modification)
logMsg logger 0 "Getting data"
getData user
writeData d
logMsg logger 0 "Done"