> Converting them to a cleaner ReaderT led to a 10% total run time regression, so I had to revert it. It makes me wonder about the speed penalty of code I designed better to begin with.<p>I have heard from other Haskellers that you can sometimes get good performance by hand-rolling an application monad, and then writing out the MonadFoo instances so you get good ergonomics:<p><pre><code> -- Instead of this:
newtype App a = App { runApp :: ReaderT AppEnv (ExceptT AppError IO) a }
deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv, MonadError AppError)
-- Try this:
newtype App a = App { runApp :: AppEnv -> IO (Either AppError a) } deriving Functor
instance Applicative App where ...
instance Monad App where ...
instance MonadIO App where ...
instance MonadReader AppEnv App where ...
instance MonadError AppError App where ...</code></pre>