----------------------------------------------------------------------------- -- | -- Module : Generics.OneLiner.Classes -- License : BSD-style (see the file LICENSE) -- -- Maintainer : sjoerd@w3future.com -- Stability : experimental -- Portability : non-portable -- ----------------------------------------------------------------------------- {-# OPTIONS -Wno-orphans #-} {-# LANGUAGE EmptyCase , LambdaCase , LinearTypes , TypeOperators , BlockArguments , MonoLocalBinds , FlexibleInstances , UndecidableInstances #-} module Generics.OneLiner.Classes where import GHC.Generics import Control.Applicative import Data.Bifunctor.Biff import Data.Bifunctor.Clown import Data.Bifunctor.Joker import Data.Bifunctor.Product import Data.Bifunctor.Tannen import Data.Functor.Contravariant import Data.Functor.Contravariant.Divisible import Data.Functor.Compose import Data.Kind (FUN) import Data.Profunctor hiding (Profunctor(..)) import qualified Data.Profunctor as P import Data.Profunctor.Linear (Profunctor(..)) import Data.Profunctor.Kleisli.Linear import Data.Tagged import Data.Unrestricted.Linear () import GHC.Types (Multiplicity(..)) import Prelude.Linear (forget) import qualified Data.Functor.Linear as DL import qualified Control.Functor.Linear as CL -- | A generic function using a `GenericRecordProfunctor` works on any data type -- with exactly one constructor, a.k.a. records, -- with multiple fields (`mult`) or no fields (`unit`). -- -- `GenericRecordProfunctor` is similar to `ProductProfuctor` from the -- product-profunctor package, but using types from GHC.Generics. class (Profunctor p, GenericUnitProfunctor p, GenericProductProfunctor p) => GenericRecordProfunctor p instance (Profunctor p, GenericUnitProfunctor p, GenericProductProfunctor p) => GenericRecordProfunctor p -- | A generic function using a `GenericNonEmptyProfunctor` works on any data -- type with at least one constructor. class (GenericRecordProfunctor p, GenericSumProfunctor p) => GenericNonEmptyProfunctor p where instance (GenericRecordProfunctor p, GenericSumProfunctor p) => GenericNonEmptyProfunctor p where -- | A generic function using a `GenericProfunctor` works on any -- algebraic data type of kind @Type@, including those with no constructors and constants. class (GenericNonEmptyProfunctor p, GenericEmptyProfunctor p) => GenericProfunctor p where instance (GenericNonEmptyProfunctor p, GenericEmptyProfunctor p) => GenericProfunctor p where -- | A generic function using a `Generic1Profunctor` works on any -- algebraic data type of kind @Type -> Type@, including those with no constructors and constants. class (GenericProfunctor p, GenericConstantProfunctor p) => Generic1Profunctor p where instance (GenericProfunctor p, GenericConstantProfunctor p) => Generic1Profunctor p where dimapForget :: P.Profunctor p => (a %1-> b) -> (c %1-> d) -> p b c -> p a d dimapForget l r = P.dimap (forget l) (forget r) class Profunctor p => GenericUnitProfunctor p where unit :: p (U1 a) (U1 a') class Profunctor p => GenericProductProfunctor p where mult :: p (f a) (f' a') -> p (g a) (g' a') -> p ((f :*: g) a) ((f' :*: g') a') class Profunctor p => GenericSumProfunctor p where plus :: p (f a) (f' a') -> p (g a) (g' a') -> p ((f :+: g) a) ((f' :+: g') a') class Profunctor p => GenericConstantProfunctor p where identity :: p c c class Profunctor p => GenericEmptyProfunctor p where zero :: p (V1 a) (V1 a') instance Profunctor (FUN 'One) where dimap f g h = \x -> g (h (f x)) instance GenericUnitProfunctor (FUN 'One) where unit U1 = U1 {-# INLINE unit #-} instance GenericProductProfunctor (FUN 'One) where mult f g (l :*: r) = f l :*: g r {-# INLINE mult #-} instance GenericSumProfunctor (FUN 'One) where plus f g = e1 (\x -> L1 (f x)) (\x -> R1 (g x)) {-# INLINE plus #-} instance GenericEmptyProfunctor (FUN 'One) where zero = \case {-# INLINE zero #-} instance GenericConstantProfunctor (FUN 'One) where identity x = x {-# INLINE identity #-} instance GenericUnitProfunctor (->) where unit U1 = U1 {-# INLINE unit #-} instance GenericProductProfunctor (->) where mult f g (l :*: r) = f l :*: g r {-# INLINE mult #-} instance GenericSumProfunctor (->) where plus f g = e1 (\x -> L1 (f x)) (\x -> R1 (g x)) {-# INLINE plus #-} instance GenericEmptyProfunctor (->) where zero = \case {-# INLINE zero #-} instance GenericConstantProfunctor (->) where identity x = x {-# INLINE identity #-} instance Profunctor Tagged where dimap = dimapForget instance GenericUnitProfunctor Tagged where unit = Tagged U1 {-# INLINE unit #-} instance GenericProductProfunctor Tagged where mult (Tagged l) (Tagged r) = Tagged $ l :*: r {-# INLINE mult #-} instance Functor f => Profunctor (Star f) where dimap = dimapForget instance Applicative f => GenericUnitProfunctor (Star f) where unit = Star $ \_ -> pure U1 {-# INLINE unit #-} instance Applicative f => GenericProductProfunctor (Star f) where mult (Star f) (Star g) = Star $ \(l :*: r) -> (:*:) <$> f l <*> g r {-# INLINE mult #-} instance Applicative f => GenericSumProfunctor (Star f) where plus (Star f) (Star g) = Star $ e1 (fmap L1 . f) (fmap R1 . g) {-# INLINE plus #-} instance Functor f => GenericEmptyProfunctor (Star f) where zero = Star \case {-# INLINE zero #-} instance Applicative f => GenericConstantProfunctor (Star f) where identity = Star pure {-# INLINE identity #-} instance DL.Applicative f => GenericUnitProfunctor (Kleisli f) where unit = Kleisli $ \U1 -> DL.pure U1 {-# INLINE unit #-} instance DL.Applicative f => GenericProductProfunctor (Kleisli f) where mult (Kleisli f) (Kleisli g) = Kleisli $ \(l :*: r) -> (:*:) DL.<$> f l DL.<*> g r {-# INLINE mult #-} instance DL.Applicative f => GenericSumProfunctor (Kleisli f) where plus (Kleisli f) (Kleisli g) = Kleisli $ e1 (\x -> DL.fmap L1 (f x)) (\x -> DL.fmap R1 (g x)) {-# INLINE plus #-} instance DL.Applicative f => GenericEmptyProfunctor (Kleisli f) where zero = Kleisli \case {-# INLINE zero #-} instance CL.Applicative f => GenericConstantProfunctor (Kleisli f) where identity = Kleisli CL.pure {-# INLINE identity #-} instance Functor f => Profunctor (Costar f) where dimap = dimapForget instance Functor f => GenericUnitProfunctor (Costar f) where unit = Costar $ const U1 {-# INLINE unit #-} instance Functor f => GenericProductProfunctor (Costar f) where mult (Costar f) (Costar g) = Costar $ \lr -> f (fst1 <$> lr) :*: g (snd1 <$> lr) {-# INLINE mult #-} instance (Functor f, Applicative g, P.Profunctor p) => Profunctor (Biff p f g) where dimap = dimapForget instance (Functor f, Applicative g, P.Profunctor p, GenericUnitProfunctor p) => GenericUnitProfunctor (Biff p f g) where unit = Biff $ P.dimap (const U1) pure unit {-# INLINE unit #-} instance (Functor f, Applicative g, P.Profunctor p, GenericProductProfunctor p) => GenericProductProfunctor (Biff p f g) where mult (Biff f) (Biff g) = Biff $ P.dimap (liftA2 (:*:) (Compose . fmap fst1) (Compose . fmap snd1)) (\(Compose l :*: Compose r) -> liftA2 (:*:) l r) (mult (P.dimap getCompose Compose f) (P.dimap getCompose Compose g)) {-# INLINE mult #-} instance Functor f => Profunctor (Joker f) where dimap = dimapForget instance Applicative f => GenericUnitProfunctor (Joker f) where unit = Joker $ pure U1 {-# INLINE unit #-} instance Applicative f => GenericProductProfunctor (Joker f) where mult (Joker l) (Joker r) = Joker $ (:*:) <$> l <*> r {-# INLINE mult #-} instance Alternative f => GenericSumProfunctor (Joker f) where plus (Joker l) (Joker r) = Joker $ L1 <$> l <|> R1 <$> r {-# INLINE plus #-} instance Alternative f => GenericEmptyProfunctor (Joker f) where zero = Joker empty {-# INLINE zero #-} instance Alternative f => GenericConstantProfunctor (Joker f) where identity = Joker empty {-# INLINE identity #-} instance Contravariant f => Profunctor (Clown f) where dimap = dimapForget instance Divisible f => GenericUnitProfunctor (Clown f) where unit = Clown conquer {-# INLINE unit #-} instance Divisible f => GenericProductProfunctor (Clown f) where mult (Clown f) (Clown g) = Clown $ divide (\(l :*: r) -> (l, r)) f g {-# INLINE mult #-} instance Decidable f => GenericSumProfunctor (Clown f) where plus (Clown f) (Clown g) = Clown $ choose (e1 Left Right) f g {-# INLINE plus #-} instance Decidable f => GenericEmptyProfunctor (Clown f) where zero = Clown $ lose \case {-# INLINE zero #-} instance Decidable f => GenericConstantProfunctor (Clown f) where identity = Clown conquer {-# INLINE identity #-} instance (Profunctor p, Profunctor q) => Profunctor (Product p q) where dimap f g (Pair l r) = Pair (dimap f g l) (dimap f g r) instance (GenericUnitProfunctor p, GenericUnitProfunctor q) => GenericUnitProfunctor (Product p q) where unit = Pair unit unit {-# INLINE unit #-} instance (GenericProductProfunctor p, GenericProductProfunctor q) => GenericProductProfunctor (Product p q) where mult (Pair l1 r1) (Pair l2 r2) = Pair (mult l1 l2) (mult r1 r2) {-# INLINE mult #-} instance (GenericSumProfunctor p, GenericSumProfunctor q) => GenericSumProfunctor (Product p q) where plus (Pair l1 r1) (Pair l2 r2) = Pair (plus l1 l2) (plus r1 r2) {-# INLINE plus #-} instance (GenericEmptyProfunctor p, GenericEmptyProfunctor q) => GenericEmptyProfunctor (Product p q) where zero = Pair zero zero {-# INLINE zero #-} instance (GenericConstantProfunctor p, GenericConstantProfunctor q) => GenericConstantProfunctor (Product p q) where identity = Pair identity identity {-# INLINE identity #-} instance (Applicative f, Profunctor p) => Profunctor (Tannen f p) where dimap f g (Tannen p) = Tannen $ dimap f g <$> p instance (Applicative f, GenericUnitProfunctor p) => GenericUnitProfunctor (Tannen f p) where unit = Tannen (pure unit) {-# INLINE unit #-} instance (Applicative f, GenericProductProfunctor p) => GenericProductProfunctor (Tannen f p) where mult (Tannen l) (Tannen r) = Tannen $ liftA2 mult l r {-# INLINE mult #-} instance (Applicative f, GenericSumProfunctor p) => GenericSumProfunctor (Tannen f p) where plus (Tannen l) (Tannen r) = Tannen $ liftA2 plus l r {-# INLINE plus #-} instance (Applicative f, GenericEmptyProfunctor p) => GenericEmptyProfunctor (Tannen f p) where zero = Tannen (pure zero) {-# INLINE zero #-} instance (Applicative f, GenericConstantProfunctor p) => GenericConstantProfunctor (Tannen f p) where identity = Tannen (pure identity) {-# INLINE identity #-} newtype Zip f a b = Zip { runZip :: a -> a -> f b } instance Functor f => Profunctor (Zip f) where dimap f g (Zip h) = Zip $ \a1 a2 -> fmap (forget g) (h (f a1) (f a2)) {-# INLINE dimap #-} instance Applicative f => GenericUnitProfunctor (Zip f) where unit = Zip $ \_ _ -> pure U1 {-# INLINE unit #-} instance Applicative f => GenericProductProfunctor (Zip f) where mult (Zip f) (Zip g) = Zip $ \(al :*: ar) (bl :*: br) -> (:*:) <$> f al bl <*> g ar br {-# INLINE mult #-} instance Alternative f => GenericSumProfunctor (Zip f) where plus (Zip f) (Zip g) = Zip h where h (L1 a) (L1 b) = fmap L1 (f a b) h (R1 a) (R1 b) = fmap R1 (g a b) h _ _ = empty {-# INLINE plus #-} instance Functor f => GenericEmptyProfunctor (Zip f) where zero = Zip \case {-# INLINE zero #-} instance Alternative f => GenericConstantProfunctor (Zip f) where identity = Zip $ \_ _ -> empty {-# INLINE identity #-} e1 :: (f a %m-> b) -> (g a %m-> b) -> (f :+: g) a %m-> b e1 f _ (L1 l) = f l e1 _ f (R1 r) = f r {-# INLINE e1 #-} fst1 :: (f :*: g) a -> f a fst1 (l :*: _) = l {-# INLINE fst1 #-} snd1 :: (f :*: g) a -> g a snd1 (_ :*: r) = r {-# INLINE snd1 #-}