monoidal-functors-0.3.0.0: src/Data/Trifunctor/Rank2/Traversable.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE StandaloneKindSignatures #-}
-- | Rank-2 traversal for higher-kinded data interpreted by a trifunctor of any variance.
module Data.Trifunctor.Rank2.Traversable
( Traversable (..),
First (..),
Second (..),
Third (..),
)
where
--------------------------------------------------------------------------------
import Control.Applicative
import Data.Bifunctor (Bifunctor (..))
import Data.Functor.Contravariant (Contravariant (..))
import Data.Isomorphism (Iso (..))
import Data.Kind (Constraint, Type)
import Data.Trifunctor.Monoidal (Monoidal, Semigroupal (..), Unital (..))
import GHC.Generics (Generic (..), Generic1, K1 (..), M1 (..), U1 (..), type (:*:) (..))
import Kindly qualified
import Prelude hiding (Traversable)
--------------------------------------------------------------------------------
-- | Higher-kinded data that distributes over any trifunctor 'Monoidal' with
-- respect to tupling, splitting each field @p a b c@ into its 'First',
-- 'Second', and 'Third' projections.
--
-- The generic default covers records whose fields all have the shape
-- @p a b c@. Sums and nested HKD fields are not supported.
class Traversable hkd where
-- | Pull the interpretation trifunctor out of the record.
sequence :: forall cat1 cat2 cat3 p. (Kindly.Trifunctor cat1 cat2 cat3 p, Kindly.LiftIso cat1, Kindly.LiftIso cat2, Kindly.LiftIso cat3, Monoidal (->) (,) () (,) () (,) () (,) () p) => hkd p -> p (hkd First) (hkd Second) (hkd Third)
default sequence ::
forall cat1 cat2 cat3 p.
( Kindly.Trifunctor cat1 cat2 cat3 p,
Kindly.LiftIso cat1,
Kindly.LiftIso cat2,
Kindly.LiftIso cat3,
Monoidal (->) (,) () (,) () (,) () (,) () p,
Generic (hkd p),
Generic (hkd First),
Generic (hkd Second),
Generic (hkd Third),
GTraversable p (Rep (hkd p)) (Rep (hkd First)) (Rep (hkd Second)) (Rep (hkd Third))
) =>
hkd p ->
p (hkd First) (hkd Second) (hkd Third)
sequence = Kindly.trimapIso (Iso to from) (Iso to from) (Iso to from) . gsequence @p @(Rep (hkd p)) @(Rep (hkd First)) @(Rep (hkd Second)) . from
type GTraversable :: (Type -> Type -> Type -> Type) -> (Type -> Type) -> (Type -> Type) -> (Type -> Type) -> (Type -> Type) -> Constraint
class GTraversable p f g h i where
gsequence :: f x -> p (g x) (h x) (i x)
instance (Kindly.Trifunctor cat1 cat2 cat3 p, Kindly.LiftIso cat1, Kindly.LiftIso cat2, Kindly.LiftIso cat3, GTraversable p f g h i) => GTraversable p (M1 _1 _2 f) (M1 _1 _2 g) (M1 _1 _2 h) (M1 _1 _2 i) where
gsequence :: M1 _1 _2 f x -> p (M1 _1 _2 g x) (M1 _1 _2 h x) (M1 _1 _2 i x)
gsequence (M1 f) = Kindly.trimapIso (Iso M1 unM1) (Iso M1 unM1) (Iso M1 unM1) $ gsequence @p @f @g @h @i f
instance (Kindly.Trifunctor cat1 cat2 cat3 p, Kindly.LiftIso cat1, Kindly.LiftIso cat2, Kindly.LiftIso cat3) => GTraversable p (K1 _1 (p a b c)) (K1 _1 (First a b c)) (K1 _1 (Second a b c)) (K1 _1 (Third a b c)) where
gsequence :: K1 _1 (p a b c) x -> p (K1 _1 (First a b c) x) (K1 _1 (Second a b c) x) (K1 _1 (Third a b c) x)
gsequence (K1 f) = Kindly.trimapIso (Iso (K1 . First) (unFirst . unK1)) (Iso (K1 . Second) (unSecond . unK1)) (Iso (K1 . Third) (unThird . unK1)) f
instance (Kindly.Trifunctor cat1 cat2 cat3 p, Kindly.LiftIso cat1, Kindly.LiftIso cat2, Kindly.LiftIso cat3, Monoidal (->) (,) () (,) () (,) () (,) () p) => GTraversable p U1 U1 U1 U1 where
gsequence :: U1 x -> p (U1 x) (U1 x) (U1 x)
gsequence U1 = Kindly.trimapIso (Iso (const U1) (const ())) (Iso (const U1) (const ())) (Iso (const U1) (const ())) $ introduce @_ @() @() @() ()
instance
(Kindly.Trifunctor cat1 cat2 cat3 p, Kindly.LiftIso cat1, Kindly.LiftIso cat2, Kindly.LiftIso cat3, Monoidal (->) (,) () (,) () (,) () (,) () p, GTraversable p f1 g1 h1 j1, GTraversable p f2 g2 h2 j2) =>
GTraversable p (f1 :*: f2) (g1 :*: g2) (h1 :*: h2) (j1 :*: j2)
where
gsequence :: (:*:) f1 f2 x -> p ((:*:) g1 g2 x) ((:*:) h1 h2 x) ((:*:) j1 j2 x)
gsequence (hkd1 :*: hkd2) =
let phkd1 = gsequence @p @f1 @g1 @h1 @j1 hkd1
phkd2 = gsequence @p @f2 @g2 @h2 @j2 hkd2
in Kindly.trimapIso (Iso (uncurry (:*:)) (\(x :*: y) -> (x, y))) (Iso (uncurry (:*:)) (\(x :*: y) -> (x, y))) (Iso (uncurry (:*:)) (\(x :*: y) -> (x, y))) $ combine @_ @(,) @(,) @(,) (phkd1, phkd2)
--------------------------------------------------------------------------------
-- | Projects a field's first argument: @First x y z@ holds the @x@.
type First :: Type -> Type -> Type -> Type
newtype First x y z = First {unFirst :: x}
deriving stock (Generic, Generic1, Functor)
deriving newtype (Bounded, Show, Read, Eq, Ord, Enum, Num, Integral, Real, Semigroup, Monoid)
instance Contravariant (First x y) where
contramap :: (a' -> a) -> First x y a -> First x y a'
contramap _ (First x) = First x
instance (Monoid x) => Applicative (First x y) where
pure :: a -> First x y a
pure _ = First mempty
liftA2 :: (a -> b -> c) -> First x y a -> First x y b -> First x y c
liftA2 _ (First x) (First x') = First (x <> x')
instance Bifunctor (First x) where
bimap :: (a -> b) -> (c -> d) -> First x a c -> First x b d
bimap _ _ (First x) = First x
--------------------------------------------------------------------------------
-- | Projects a field's second argument: @Second x y z@ holds the @y@.
type Second :: Type -> Type -> Type -> Type
newtype Second x y z = Second {unSecond :: y}
deriving stock (Generic, Generic1, Functor)
deriving newtype (Bounded, Show, Read, Eq, Ord, Enum, Num, Integral, Real, Semigroup, Monoid)
instance (Monoid y) => Applicative (Second x y) where
pure :: a -> Second x y a
pure _ = Second mempty
liftA2 :: (a -> b -> c) -> Second x y a -> Second x y b -> Second x y c
liftA2 _ (Second y) (Second y') = Second (y <> y')
instance Bifunctor (Second x) where
bimap :: (a -> b) -> (c -> d) -> Second x a c -> Second x b d
bimap f _ (Second a) = Second $ f a
--------------------------------------------------------------------------------
-- | Projects a field's third argument: @Third x y z@ holds the @z@.
type Third :: Type -> Type -> Type -> Type
newtype Third x y z = Third {unThird :: z}
deriving stock (Generic, Generic1, Functor)
deriving newtype (Bounded, Show, Read, Eq, Ord, Enum, Num, Integral, Real, Semigroup, Monoid)
instance Applicative (Third x y) where
pure :: a -> Third x y a
pure = Third
liftA2 :: (a -> b -> c) -> Third x y a -> Third x y b -> Third x y c
liftA2 f (Third y) (Third y') = Third (f y y')
instance Bifunctor (Third x) where
bimap :: (a -> b) -> (c -> d) -> Third x a c -> Third x b d
bimap _ g (Third y) = Third (g y)