Scale vs architecture

I’m unsure, as I’ve never tested it formally and I’m still trying to wrap my head around all of the options.

Right now, I’m using my Haskell-Snake-Leaderboard-Replay-Database learning project to experiment with and mix and match multiple ideas and systems.

In every case, the most guiding advice I’ve ever received was:

This kinda of passing fundamental handles/basic values to every function makes it pretty trivial in my experience to refactor from one style into another because your core logical functions that don’t need to do anything effectful can just be reused everywhere.

So long as you can get the a to the function that needs it, you’re good.

My current problem right now is figuring out how to do logging but to different handlers and how to combine handlers, trying to do it myself so I get an intuition for the design of how to attach logging to my otherwise pure functions.

Right now, what I’ve been exploring trying to model is passing a Logger handle to each function: I know an easy way to do this may be with IORef/MVar [String/Text]. WriterT/StateT require me to bake it as part of some data structure (I’ve heard Writer there’s major perfomance/space implications with Writer.) I think I can pass WriterT/StateT handles around, but I will need to always exec their functions to get the final result: which I’m fine with - I did something similar for using Bluefin.State for logging my GameState

Functions that look like these is what I’ve been trying to model in all the different styles:

data Logger phantom = Logger { mkLogger :: someAccumulationType } -- Phantom could maybe help me not have incompatible actions writing to some loggers?

program :: IO () -- (?)
program = do
  Logger loggerIO <- (pure (mkLogger logIOHandle) :: Logger IO)
  Logger loggerint <- (pure (mkLogger logIntHandle) :: Logger Int)
  ...
  ...
  logAction (putStrLn "Hello") "Printing Hello" loggerIO
  res <- logAction (pure (2 + 2)) "Adding two numbers" loggerInt
  putStrLn $ "The result of adding the numbers was " <> show res 

logAction :: Monad m => m a -> Text -> Logger p -> m a
logAction action logmsg logger = do
  a <- (lift?) action
  writeToLog logger logmsg
  pure a

writeToLog = undefined -- dependent on what the handle is from the looks of things