{-# LANGUAGE ConstraintKinds #-} -- For type class synonyms {-# LANGUAGE DefaultSignatures #-} -- For adding Trans* constraints module Symantic.Univariant.Trans where -- TODO: move to symantic-univariant import Data.Function ((.)) import Data.Kind (Type) -- * Type family 'Output' type family Output (repr :: Type -> Type) :: Type -> Type -- * Class 'Trans' -- | A 'trans'lation from an interpreter @(from)@ to an interpreter @(to)@. class Trans from to where trans :: from a -> to a -- * Class 'BiTrans' -- | Convenient type class synonym. -- Note that this is not necessarily a bijective 'trans'lation, a 'trans' being not necessarily injective nor surjective. type BiTrans from to = (Trans from to, Trans to from) -- ** Class 'Liftable' -- | Convenient type class synonym for using 'Output' type Liftable repr = Trans (Output repr) repr lift :: forall repr a. Liftable repr => Output repr a -> repr a lift = trans @(Output repr) {-# INLINE lift #-} unlift :: forall repr a. Trans repr (Output repr) => repr a -> Output repr a unlift = trans @repr {-# INLINE unlift #-} -- ** Class 'Unliftable' -- | Convenient type class synonym for using 'Output' type Unliftable repr = Trans repr (Output repr) -- * Class 'Trans1' class Trans1 from to where trans1 :: (from a -> from b) -> to a -> to b default trans1 :: BiTrans from to => (from a -> from b) -> to a -> to b trans1 f = trans . f . trans {-# INLINE trans1 #-} -- ** Class 'Liftable1' -- | Convenient type class synonym for using 'Output' type Liftable1 repr = Trans1 (Output repr) repr lift1 :: forall repr a b. Liftable1 repr => (Output repr a -> Output repr b) -> repr a -> repr b lift1 = trans1 @(Output repr) {-# INLINE lift1 #-} -- * Class 'Trans2' class Trans2 from to where trans2 :: (from a -> from b -> from c) -> to a -> to b -> to c default trans2 :: BiTrans from to => (from a -> from b -> from c) -> to a -> to b -> to c trans2 f a b = trans (f (trans a) (trans b)) {-# INLINE trans2 #-} -- ** Class 'Liftable2' -- | Convenient type class synonym for using 'Output' type Liftable2 repr = Trans2 (Output repr) repr lift2 :: forall repr a b c. Liftable2 repr => (Output repr a -> Output repr b -> Output repr c) -> repr a -> repr b -> repr c lift2 = trans2 @(Output repr) {-# INLINE lift2 #-} -- * Class 'Trans3' class Trans3 from to where trans3 :: (from a -> from b -> from c -> from d) -> to a -> to b -> to c -> to d default trans3 :: BiTrans from to => (from a -> from b -> from c -> from d) -> to a -> to b -> to c -> to d trans3 f a b c = trans (f (trans a) (trans b) (trans c)) {-# INLINE trans3 #-} -- ** Class 'Liftable3' -- | Convenient type class synonym for using 'Output' type Liftable3 repr = Trans3 (Output repr) repr lift3 :: forall repr a b c d. Liftable3 repr => (Output repr a -> Output repr b -> Output repr c -> Output repr d) -> repr a -> repr b -> repr c -> repr d lift3 = trans3 @(Output repr) {-# INLINE lift3 #-} -- * Type 'Any' -- | A newtype to disambiguate the 'Trans' instance to any other interpreter when there is also one or more 'Trans's to other interpreters with a different interpretation than the generic one. newtype Any repr a = Any { unAny :: repr a } type instance Output (Any repr) = repr instance Trans (Any repr) repr where trans = unAny instance Trans1 (Any repr) repr instance Trans2 (Any repr) repr instance Trans3 (Any repr) repr instance Trans repr (Any repr) where trans = Any instance Trans1 repr (Any repr) instance Trans2 repr (Any repr) instance Trans3 repr (Any repr)