{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main where
import          Counter
import          Weather
import          MockWeather
import          MockCounter
import          Control.Monad.Trans.Class
-- 'someApp' works for any underlying monad 'm' 
-- and any instance of 'MonadCounter'
-- and any instance of 'MonadWeather'
someApp :: (Monad m, MonadCounter m , MonadWeather m) => m String
someApp = do
  _ <- increment
  (WeatherData weather) <- byCity "Amsterdam"
  return weather
newtype MyAppM m a = MyAppM { unMyAppM :: MockCounter (MockWeather m) a }
  deriving (Functor, Applicative, Monad, MonadCounter, MonadWeather)
instance MonadTrans MyAppM where
  lift = MyAppM . lift . lift
runMyAppM :: Int -> MyAppM m a -> m (a, Int)
runMyAppM i = runMockWeather . (`runMockCounter` i) . unMyAppM
-- set the underlying monad to 'IO'
-- and 'MonadCounter' instance to 'MockCounter'
-- and 'MonadWeather' instance to 'MockWeather'
main :: IO ()
main = runMyAppM 12 (someApp >> someApp) >>= print
{-# LANGUAGE DefaultSignatures          #-}
{-# LANGUAGE GADTs                      #-}
module Weather where
import           Control.Monad.Trans.Class
newtype WeatherData = WeatherData String deriving (Show)
class Monad m => MonadWeather m where
  byCity :: String -> m WeatherData
  default byCity :: (MonadTrans t, MonadWeather m', m ~ t m') => String -> m WeatherData
  byCity = lift . byCity
{-# LANGUAGE DefaultSignatures          #-}
{-# LANGUAGE GADTs                      #-}
module Counter where
import           Control.Monad.Trans.Class
class Monad m => MonadCounter m where
  increment :: m Int
  current :: m Int
  default increment :: (MonadTrans t, MonadCounter m', m ~ t m') => m Int
  increment = lift increment
  default current :: (MonadTrans t, MonadCounter m', m ~ t m') => m Int
  current = lift current
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module MockWeather where
import           Weather
import           Counter
import           Control.Monad.Trans.Class
import           Control.Monad.Trans.Identity
import           Control.Monad.IO.Class
newtype MockWeather m a = MockWeather {
  unMockWeather :: IdentityT m a
} deriving (Functor, Applicative, Monad, MonadTrans, MonadIO)
runMockWeather :: MockWeather f a -> f a
runMockWeather = runIdentityT . unMockWeather
instance MonadCounter m => MonadCounter (MockWeather m)
instance Monad m => MonadWeather (MockWeather m) where
  byCity city = MockWeather $ return $ WeatherData $ "It is sunny in " ++ city
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module MockCounter where
import           Weather
import           Counter
import           Control.Monad.Trans.Class
import           Control.Monad.State
import           Control.Monad.Identity
newtype MockCounter m a = MockCounter {
  unMockCounter :: StateT Int m a
} deriving (Functor, Applicative, Monad, MonadTrans, MonadState Int, MonadIO)
instance MonadWeather m => MonadWeather (MockCounter m)
defaultMockCounter :: MockCounter Identity ()
defaultMockCounter = MockCounter $ put 0
runMockCounter :: MockCounter m a -> Int -> m (a, Int)
runMockCounter = runStateT . unMockCounter
instance Monad m => MonadCounter (MockCounter m) where
  increment = MockCounter $ do
    c <- get
    let n = c + 1
    put n
    return n
  current = MockCounter get