dep-t-0.4.0.0: test/tests_curry.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DerivingStrategies #-}
module Main (main) where
import Control.Monad.Dep
import Control.Monad.Reader
import Control.Monad.Writer
import Data.Kind
import Data.Coerce
import Data.List (intercalate)
import Rank2 qualified
import Rank2.TH qualified
import Test.Tasty
import Test.Tasty.HUnit
import Prelude hiding (log)
import Data.SOP
import Data.SOP.NP
type Multicurryable ::
[Type] ->
((Type -> Type) -> Type) ->
(Type -> Type) ->
Type ->
Type ->
Constraint
class Multicurryable as e m r curried | curried -> as e m r where
type DownToBaseMonad as e m r curried :: Type
multiuncurry :: curried -> NP I as -> DepT e m r
multicurry :: (NP I as -> DepT e m r) -> curried
_runFromEnv :: m (e (DepT e m)) -> (e (DepT e m) -> curried) -> DownToBaseMonad as e m r curried
instance Monad m => Multicurryable '[] e m r (DepT e m r) where
type DownToBaseMonad '[] e m r (DepT e m r) = m r
multiuncurry action Nil = action
multicurry f = f Nil
_runFromEnv producer extractor = do
e <- producer
runDepT (extractor e) e
instance Multicurryable as e m r curried => Multicurryable (a ': as) e m r (a -> curried) where
type DownToBaseMonad (a ': as) e m r (a -> curried) = a -> DownToBaseMonad as e m r curried
multiuncurry f (I a :* as) = multiuncurry @as @e @m @r @curried (f a) as
multicurry f a = multicurry @as @e @m @r @curried (f . (:*) (I a))
_runFromEnv producer extractor a = _runFromEnv @as @e @m @r @curried producer (\f -> extractor f a)
class Multicurryable as e m r curried => Deceivable as newtyped e m r curried where
type Deceive as newtyped e m r curried :: Type
deceive :: (e (DepT e m) -> newtyped) -> Deceive as newtyped e m r curried -> curried
instance Monad m => Deceivable '[] newtyped e m r (DepT e m r) where
type Deceive '[] newtyped e m r (DepT e m r) = ReaderT newtyped m r
deceive f action = DepT (withReaderT f action)
instance Deceivable as newtyped e m r curried => Deceivable (a ': as) newtyped e m r (a -> curried) where
type Deceive (a ': as) newtyped e m r (a -> curried) = a -> Deceive as newtyped e m r curried
deceive f g a = deceive @as @newtyped @e @m @r f (g a)
--
--
--
type HasLogger :: (Type -> Type) -> Type -> Constraint
class HasLogger m em | em -> m where
logger :: em -> String -> m ()
type HasIntermediate :: (Type -> Type) -> Type -> Constraint
class HasIntermediate m em | em -> m where
intermediate :: em -> String -> m ()
type Env :: (Type -> Type) -> Type
data Env m = Env
{ _logger1 :: String -> m (),
_logger2 :: String -> m (),
_intermediate :: String -> m (),
_controllerA :: Int -> m (),
_controllerB :: Int -> m ()
}
instance HasLogger m (Env m) where
logger = _logger1
instance HasIntermediate m (Env m) where
intermediate = _intermediate
newtype Switcheroo m = Switcheroo (Env m)
deriving newtype (HasIntermediate m)
instance HasLogger m (Switcheroo m) where
logger (Switcheroo e) = _logger2 e
envW :: Env (DepT Env (Writer [String]))
envW = Env
{
_logger1 =
\_ -> tell ["logger 1"],
_logger2 =
\_ -> tell ["logger 2"],
_intermediate =
\_ -> do e <- ask ; liftD $ logger e "foo",
_controllerA =
\_ -> do e <- ask; liftD $ logger e "foo" ; liftD $ intermediate e "foo",
_controllerB =
deceive Switcheroo $
\_ -> do e <- ask; liftD $ logger e "foo" ; liftD $ intermediate e "foo"
}
_controllerA' :: MonadDep '[HasLogger, HasIntermediate] d e m => String -> m ()
_controllerA' = \_ -> do e <- ask; liftD $ logger e "foo" ; liftD $ intermediate e "foo"
_intermediate' :: MonadDep '[HasLogger] d e m => String -> m ()
_intermediate' = \_ -> do e <- ask ; liftD $ logger e "foo"
--
--
tests :: TestTree
tests =
testGroup
"All"
[
testCase "undeceived" $
assertEqual "" ["logger 1", "logger 1"] $
execWriter $ (do e <- ask; _controllerA e 7) `runDepT` envW,
testCase "deceived" $
assertEqual "" ["logger 2", "logger 1"] $
execWriter $ (do e <- ask; _controllerB e 7) `runDepT` envW
]
main :: IO ()
main = defaultMain tests