Fazendo injeção de dependência usando pilhas de mônada

Eu estoutentando abordagens diferentes para fazer o que às vezes é conhecido como injeção de dependência. Para isso, elaborei um exemplo simples de um aplicativo climático, no qual queremos buscar os dados climáticos (de um serviço da Web ou de um dispositivo de hardware), armazenar os dados climáticos (pode ser um banco de dados ou simplesmente um arquivo), e relate (imprima na tela ou fale sobre o tempo). A idéia é escrever um programa que use algunsfetch, storeereport funções, cujas implementações podem variar.

Consegui separar preocupações e abstrair das implementações de recuperação, armazenamento e relatório usandofunções emônadas livres, no entanto, a solução que cheguei com pilhas de mônad parece ruim:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module WeatherReporterMTL where

import           Control.Monad.IO.Class
import           Control.Monad.Trans.Class

type WeatherData = String

class Monad m => WeatherService m where
    fetch :: m WeatherData

class Monad m => Storage m where
    store :: WeatherData -> m ()

class Monad m => Reporter m where
    report :: WeatherData -> m ()

-- | A dummy implementation of the @WeatherService@
newtype DummyService m a = DummyService { runDummyService :: m a }
    deriving (Functor, Applicative, Monad, MonadIO)

instance MonadIO m => WeatherService (DummyService m) where
    fetch = return "won't get any warmer in December."

-- | A dummy implementation of the @Storage@
newtype DummyStorage m a = DummyStorage { runDummyStorage :: m a }
    deriving (Functor, Applicative, Monad, MonadIO, WeatherService)

-- It seems wrong that the storage has to be an instance the weather service
-- (@WeatherService@) ...

instance MonadIO m => Storage (DummyStorage m) where
    store d = liftIO $ putStrLn $ "No room left for this report: " ++ d

-- | A dummy implementation of the @Reporter@
newtype DummyReporter m a = DummyReporter { runDummyReporter :: m a }
    deriving (Functor, Applicative, Monad, MonadIO, WeatherService, Storage)

-- Ok, now this seems even worse: we're putting information about
-- how we're gonna stack our monads :/

instance MonadIO m => Reporter (DummyReporter m) where
    report d = liftIO $ putStrLn $ "Here at the MTL side " ++ d

reportWeather :: (WeatherService m, Storage m, Reporter m) => m ()
reportWeather = do
    w <- fetch
    store w
    report w

dummyWeatherReport :: IO ()
dummyWeatherReport = runDummyService $ runDummyStorage $ runDummyReporter reportWeather

No código acima, ambosDummyStorage eDummyReporter tem que ter instâncias triviais paraWeatherService, o que parece claramente errado. Além disso, essas instâncias dependem da ordem em que as mônadas são empilhadas no final. Existe uma maneira de evitar o vazamento de informações entre as diferentes pilhas?

questionAnswers(1)

yourAnswerToTheQuestion