import Control.Monad.Reader data Env = Env { counter :: !Int, flag :: !Bool } deriving (Show, Read) class Environment r where getCounter :: r -> Int getFlag :: r -> Bool instance Environment Env where getCounter = counter getFlag = flag newtype App m a = App {run :: ReaderT Env m a} deriving (Functor, Applicative, Monad, MonadIO, MonadReader Env) runApp :: Env -> App IO a -> IO a runApp e (App r) = runReaderT r e f1 :: (Environment e) => e -> IO () f1 e = putStrLn $ (("Counter: " ++) . show . getCounter) e f2 :: (Environment e) => e -> IO () f2 e = putStrLn $ (("Counter: " ++) . show . (+ 4) . getCounter) e f4 :: App IO () f4 = do env <- ask liftIO $ do f1 env f2 env main :: IO () main = runReaderT (run f4) (Env {counter = 0, flag = False})