
Hi, I hope to get some advice on how to structure an application. So I acquire a handle early on that I use all over the app, but I don't want to pass the handle itself around, but wrap the handle with "commands" that a) make a nicer api and/or b) only allow specific usecases of the handle. I tried and failed to use MonadReader in a straightforward way and now I'm wondering what options there are. Looking forward to your feedback, Best, Tilmann module Main where import Control.Monad import Control.Monad.Reader import Graphics.UI.WX import System.IO -- imagine many more commands like this one ping :: (MonadReader Handle m, MonadIO m) => m () ping = do h <- ask liftIO $ hPutStrLn h "ping" main :: IO () main = do let h = stdout -- in the real app, this handle isn't stdout of course but opened separately start $ runReaderT wxApp h wxApp :: (MonadReader Handle m, MonadIO m) => m () wxApp = do ping -- this works, but I don't need it here.. liftIO $ do f <- frame [ ] timer f [ interval := 1000 -- , on command := hputStrLn h "ping" -- this is what I try to avoid -- , on command := ping -- of course, this doesn't work, but it would be so nice.. , enabled := True] return () -- Alternatively main2 :: IO () main2 = do let h = stdout start $ runReaderT wxApp2 (mkCommands h) wxApp2 :: (MonadReader Commands m, MonadIO m) => m () wxApp2 = do commands <- ask liftIO $ do f <- frame [ ] timer f [ interval := 1000 , on command := ping2 commands , enabled := True] return () data Commands = Commands { ping2 :: IO () -- .. many more } mkCommands :: Handle -> Commands mkCommands h = Commands (hPutStrLn h "ping")