{-# language DataKinds #-} {-# language PolyKinds #-} {-# language GADTs #-} {-# language TypeOperators #-} {-# language MultiParamTypeClasses #-} {-# language ScopedTypeVariables #-} {-# language FunctionalDependencies #-} {-# language TypeApplications #-} {-# language AllowAmbiguousTypes #-} {-# language TypeFamilies #-} -- | Poly-kinded 'Functor' type class. -- 'KFunctor' generalizes functors, bifunctors, profunctors, ... -- by declaring a list of 'Variance's for a type constructor. module Data.PolyKinded.Functor where import Data.PolyKinded -- | Declares that the type constructor @f@ is a generalized -- functor whose variances for each type argument are given by @v@. class KFunctor (f :: k) (v :: Variances) (as :: LoT k) (bs :: LoT k) | f -> v where -- | The generalized version of 'fmap', 'bimap', 'dimap', and so on. kfmap :: Mappings v as bs -> f :@@: as -> f :@@: bs -- | The generalized version of 'fmap', 'bimap', 'dimap', and so on. -- This version uses 'Split' to obtain better type inference. kmapo :: forall f v as bs. (KFunctor f v as bs) => Mappings v as bs -> f :@@: as -> f :@@: bs kmapo = kfmap @_ @f -- ** Mappings of different variance -- | Possible variances for each argument of a type constructor. data Variance = Co -- ^ The functor is covariant in this position. | Contra -- ^ The functor is contravariant in this position. | Phantom -- ^ This position is not used in any constructor. type Variances = [Variance] -- | If a 'KFunctor' needs to map an @f ... a ...@ to an @f ... b ...@, -- a @Mapping v a b@ specifies which function needs to be provided -- for that position depending on its variance @v@. type family Mapping (v :: Variance) a b where Mapping 'Co a b = a -> b Mapping 'Contra a b = b -> a Mapping 'Phantom a b = () infixr 5 :^: -- | List of mappings for the list of variances @v@. data Mappings (v :: Variances) (x :: LoT k) (y :: LoT k) where M0 :: Mappings '[] 'LoT0 'LoT0 (:^:) :: Mapping v a b -> Mappings vs as bs -> Mappings (v ': vs) (a ':&&: as) (b ':&&: bs)