Hacer inyección de dependencia usando pilas de mónada

estoyprobando diferentes enfoques hacer lo que a veces se conoce como inyección de dependencia. Para esto, he elaborado un ejemplo simple de una aplicación meteorológica, donde queremos obtener los datos meteorológicos (de un servicio web o de un dispositivo de hardware), almacenar los datos meteorológicos (podría ser una base de datos o simplemente un archivo), e informarlo (ya sea imprimirlo en la pantalla o hablar sobre el clima). La idea es escribir un programa que use algunosfetch, storeyreport funciones, cuyas implementaciones pueden variar.

Me las arreglé para separar las preocupaciones y abstraerme de las implementaciones de recuperación, almacenamiento e informes utilizandofunciones ymónadas libres, sin embargo, la solución a la que llegué con monad montones se ve mal:

{-# 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

En el código anterior, ambosDummyStorage yDummyReporter tener que tener instancias triviales paraWeatherService, lo que parece claramente incorrecto. Además, estas instancias dependen del orden en que las mónadas se apilan al final. ¿Hay alguna manera de evitar filtrar información entre las diferentes pilas?

Respuestas a la pregunta(1)

Su respuesta a la pregunta