{-# LANGUAGE ScopedTypeVariables, TypeFamilies, FlexibleInstances #-} {-# LANGUAGE UndecidableInstances, TypeOperators, MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications, RankNTypes, DataKinds, ViewPatterns #-} {-# LANGUAGE EmptyCase, FlexibleContexts, AllowAmbiguousTypes #-} {-# OPTIONS_GHC -fno-warn-missing-methods #-} module Control.Effects.Generic where import qualified GHC.Generics as Gen import GHC.Generics import Control.Monad.Trans import GHC.TypeLits import Control.Monad (join) class (Generic (a m), Generic (a (t m)), SimpleMethodsRep (Rep (a m)) (Rep (a (t m))) m t) => SimpleMethods a (m :: * -> *) (t :: (* -> *) -> * -> *) where liftSimple :: a m -> a (t m) instance (Generic (a m), Generic (a (t m)), SimpleMethodsRep (Rep (a m)) (Rep (a (t m))) m t) => SimpleMethods a m t where liftSimple = Gen.to . liftSimpleRep @(Rep (a m)) @(Rep (a (t m))) @m @t . Gen.from class SimpleMethodsRep r r' (m :: * -> *) (t :: (* -> *) -> * -> *) where liftSimpleRep :: r x -> r' x instance SimpleMethodsRep c c' m t => SimpleMethodsRep (M1 a b c) (M1 a b c') m t where liftSimpleRep (M1 x) = M1 (liftSimpleRep @c @c' @m @t x) instance (SimpleMethodsRep a a' m t, SimpleMethodsRep b b' m t) => SimpleMethodsRep (a :*: b) (a' :*: b') m t where liftSimpleRep (a :*: b) = liftSimpleRep @a @a' @m @t a :*: liftSimpleRep @b @b' @m @t b instance (SimpleMethodsRep a a' m t, SimpleMethodsRep b b' m t) => SimpleMethodsRep (a :+: b) (a' :+: b') m t where liftSimpleRep (L1 a) = L1 (liftSimpleRep @a @a' @m @t a) liftSimpleRep (R1 b) = R1 (liftSimpleRep @b @b' @m @t b) instance SimpleMethodsRep U1 U1 m t where liftSimpleRep U1 = U1 instance SimpleMethodsRep V1 V1 m t where liftSimpleRep v = case v of {} instance SimpleMethod a a' m t => SimpleMethodsRep (K1 x a) (K1 x a') m t where liftSimpleRep (K1 m) = K1 (liftMethod @a @a' @m @t m) class SimpleMethod f ft (m :: * -> *) (t :: (* -> *) -> * -> *) where liftMethod :: f -> ft instance SimpleMethod f ft m t => SimpleMethod (a -> f) (a -> ft) m t where liftMethod f a = liftMethod @f @ft @m @t (f a) instance {-# OVERLAPPABLE #-} ForceError (TypeError ('Text "Parameters of methods can't depend on the monadic context." ':$$: 'Text "The parameter `" ':<>: 'ShowType a ':<>: 'Text "` depends on `" ':<>: 'ShowType m ':<>: 'Text "`")) => SimpleMethod (a -> f) (a' -> ft) m t where liftMethod = error "Unreachable" instance (MonadTrans t, Monad m) => SimpleMethod (m a) (t m a) m t where liftMethod m = lift m instance {-# OVERLAPPABLE #-} ForceError (TypeError ('Text "The result of all methods must be monadic." ':$$: 'Text "One of the methods' result is of type `" ':<>: 'ShowType a ':<>: 'Text "`. Maybe try `" ':<>: 'ShowType (m a) ':<>: 'Text "` instead.")) => SimpleMethod a b m t where liftMethod = error "Unreachable" genericLiftThrough :: forall t e m. (MonadTrans t, Monad m, Monad (t m), SimpleMethods e m t) => e m -> e (t m) genericLiftThrough = liftSimple {-# INLINE genericLiftThrough #-} class (Generic (a m), MonadicMethodsRep (Rep (a m)) m, Monad m) => MonadicMethods a m where mergeMonadicMethods :: m (a m) -> a m instance (Generic (a m), MonadicMethodsRep (Rep (a m)) m, Monad m) => MonadicMethods a m where mergeMonadicMethods m = Gen.to (mergeMonadicMethodsRep @(Rep (a m)) @m (fmap Gen.from m)) class MonadicMethodsRep r m where mergeMonadicMethodsRep :: m (r x) -> r x instance (MonadicMethodsRep c m, Functor m) => MonadicMethodsRep (M1 a b c) m where mergeMonadicMethodsRep m = M1 (mergeMonadicMethodsRep @c @m (fmap unM1 m)) instance (MonadicMethodsRep a m, MonadicMethodsRep b m, Functor m) => MonadicMethodsRep (a :*: b) m where mergeMonadicMethodsRep m = mergeMonadicMethodsRep @a @m (fmap l m) :*: mergeMonadicMethodsRep @b @m (fmap r m) where l (x :*: _) = x r (_ :*: x) = x instance ForceError (TypeError ('Text "Can't automatically derive Effect instance for an effect with multiple constructors.")) => MonadicMethodsRep (a :+: b) m where mergeMonadicMethodsRep = error "Unreachable" instance MonadicMethodsRep U1 m where mergeMonadicMethodsRep _ = U1 instance ForceError (TypeError ('Text "Can't automatically derive Effect instance for an effect with no constructors.")) => MonadicMethodsRep V1 m where mergeMonadicMethodsRep = error "Unreachable" instance (MonadicMethod a m, Functor m) => MonadicMethodsRep (K1 x a) m where mergeMonadicMethodsRep m = K1 (mergeMonadicMethod @a @m (fmap unK1 m)) class MonadicMethod a m where mergeMonadicMethod :: m a -> a instance {-# INCOHERENT #-} (MonadicMethod b m, Functor m) => MonadicMethod (a -> b) m where mergeMonadicMethod m a = mergeMonadicMethod (fmap ($ a) m) instance Monad m => MonadicMethod (m a) m where mergeMonadicMethod = join instance {-# OVERLAPPABLE #-} ForceError (TypeError ('Text "The result of all methods must be monadic." ':$$: 'Text "One of the methods' result is of type `" ':<>: 'ShowType a ':<>: 'Text "`. Maybe try `" ':<>: 'ShowType (m a) ':<>: 'Text "` instead.")) => MonadicMethod a m where mergeMonadicMethod = error "Unreachable" genericMergeContext :: MonadicMethods a m => m (a m) -> a m genericMergeContext = mergeMonadicMethods {-# INLINE genericMergeContext #-} class ForceError (x :: *)