{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Barbie.Internal.Product
( ProductB(buniq, bprod)
, bzip, bunzip, bzipWith, bzipWith3, bzipWith4
, (/*/), (/*)
, CanDeriveProductB
, GProductB(..)
, gbprodDefault, gbuniqDefault
)
where
import Data.Barbie.Internal.Functor (FunctorB (..))
import Data.Functor.Prod
import Data.Functor.Product (Product (..))
import Data.Kind (Type)
import Data.Proxy (Proxy (..))
import Data.Generics.GenericN
class FunctorB b => ProductB (b :: (k -> Type) -> Type) where
bprod :: b f -> b g -> b (f `Product` g)
buniq :: (forall a . f a) -> b f
default bprod :: CanDeriveProductB b f g => b f -> b g -> b (f `Product` g)
bprod = gbprodDefault
default buniq :: CanDeriveProductB b f f => (forall a . f a) -> b f
buniq = gbuniqDefault
bzip :: ProductB b => b f -> b g -> b (f `Product` g)
bzip = bprod
bunzip :: ProductB b => b (f `Product` g) -> (b f, b g)
bunzip bfg = (bmap (\(Pair a _) -> a) bfg, bmap (\(Pair _ b) -> b) bfg)
bzipWith :: ProductB b => (forall a. f a -> g a -> h a) -> b f -> b g -> b h
bzipWith f bf bg
= bmap (\(Pair fa ga) -> f fa ga) (bf `bprod` bg)
bzipWith3
:: ProductB b
=> (forall a. f a -> g a -> h a -> i a)
-> b f -> b g -> b h -> b i
bzipWith3 f bf bg bh
= bmap (\(Pair (Pair fa ga) ha) -> f fa ga ha)
(bf `bprod` bg `bprod` bh)
bzipWith4
:: ProductB b
=> (forall a. f a -> g a -> h a -> i a -> j a)
-> b f -> b g -> b h -> b i -> b j
bzipWith4 f bf bg bh bi
= bmap (\(Pair (Pair (Pair fa ga) ha) ia) -> f fa ga ha ia)
(bf `bprod` bg `bprod` bh `bprod` bi)
type CanDeriveProductB b f g
= ( GenericN (b f)
, GenericN (b g)
, GenericN (b (f `Product` g))
, GProductB f g (RepN (b f)) (RepN (b g)) (RepN (b (f `Product` g)))
)
(/*/)
:: ProductB b => b f -> b g -> b (Prod '[f, g])
l /*/ r
= bmap (\(Pair f g) -> Cons f (Cons g Unit)) (l `bprod` r)
infixr 4 /*/
(/*) :: ProductB b => b f -> b (Prod fs) -> b (Prod (f ': fs))
l /* r =
bmap (\(Pair f fs) -> oneTuple f `prod` fs) (l `bprod` r)
infixr 4 /*
gbprodDefault
:: forall b f g
. CanDeriveProductB b f g
=> b f -> b g -> b (f `Product` g)
gbprodDefault l r
= toN $ gbprod @f @g (fromN l) (fromN r)
{-# INLINE gbprodDefault #-}
gbuniqDefault:: forall b f . CanDeriveProductB b f f => (forall a . f a) -> b f
gbuniqDefault x
= toN (gbuniq @f @f @_ @(RepN (b f)) @(RepN (b (f `Product` f))) x)
{-# INLINE gbuniqDefault #-}
class GProductB (f :: k -> *) (g :: k -> *) repbf repbg repbfg where
gbprod :: repbf x -> repbg x -> repbfg x
gbuniq :: (forall a . f a) -> repbf x
instance GProductB f g repf repg repfg => GProductB f g (M1 i c repf)
(M1 i c repg)
(M1 i c repfg) where
gbprod (M1 l) (M1 r) = M1 (gbprod @f @g l r)
{-# INLINE gbprod #-}
gbuniq x = M1 (gbuniq @f @g @repf @repg @repfg x)
{-# INLINE gbuniq #-}
instance GProductB f g U1 U1 U1 where
gbprod U1 U1 = U1
{-# INLINE gbprod #-}
gbuniq _ = U1
{-# INLINE gbuniq #-}
instance
( GProductB f g lf lg lfg
, GProductB f g rf rg rfg
) => GProductB f g (lf :*: rf)
(lg :*: rg)
(lfg :*: rfg) where
gbprod (l1 :*: l2) (r1 :*: r2)
= (l1 `lprod` r1) :*: (l2 `rprod` r2)
where
lprod = gbprod @f @g
rprod = gbprod @f @g
{-# INLINE gbprod #-}
gbuniq x = (gbuniq @f @g @lf @lg @lfg x :*: gbuniq @f @g @rf @rg @rfg x)
{-# INLINE gbuniq #-}
type P0 = Param 0
instance GProductB f g (Rec (P0 f a) (f a))
(Rec (P0 g a) (g a))
(Rec (P0 (f `Product` g) a) ((f `Product` g) a)) where
gbprod (Rec (K1 fa)) (Rec (K1 ga))
= Rec (K1 (Pair fa ga))
{-# INLINE gbprod #-}
gbuniq x = Rec (K1 x)
{-# INLINE gbuniq #-}
instance
( SameOrParam b b'
, ProductB b'
) => GProductB f g (Rec (b (P0 f)) (b' f))
(Rec (b (P0 g)) (b' g))
(Rec (b (P0 (f `Product` g))) (b' (f `Product` g))) where
gbprod (Rec (K1 bf)) (Rec (K1 bg))
= Rec (K1 (bf `bprod` bg))
{-# INLINE gbprod #-}
gbuniq x = Rec (K1 (buniq x))
{-# INLINE gbuniq #-}
instance ProductB Proxy where
bprod _ _ = Proxy
{-# INLINE bprod #-}
buniq _ = Proxy
{-# INLINE buniq #-}
instance (ProductB a, ProductB b) => ProductB (Product a b) where
bprod (Pair ll lr) (Pair rl rr) = Pair (bprod ll rl) (bprod lr rr)
{-# INLINE bprod #-}
buniq x = Pair (buniq x) (buniq x)
{-# INLINE buniq #-}