Redefine bifunctors in terms of product categories.

master
James T. Martin 2021-03-01 22:20:17 -08:00
parent 05ddc84fff
commit 8a1fad57df
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
7 changed files with 165 additions and 44 deletions

View File

@ -10,10 +10,11 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Install latest Haskell Stack - name: Install Haskell toolchain
uses: haskell/actions/setup@v1 uses: haskell/actions/setup@v1
with: with:
ghc-version: '9.0.1' ghc-version: '9.0.1'
cabal-version: '3.4.0.0'
- name: Build - name: Build
run: cabal v2-build run: cabal v2-build

View File

@ -28,6 +28,7 @@ library
Category.Groupoid Category.Groupoid
Category.Monoid Category.Monoid
Category.Monoidal Category.Monoidal
Category.Product
Category.Semigroup Category.Semigroup
Data.Dict Data.Dict
Data.Fin Data.Fin
@ -88,6 +89,7 @@ library
TupleSections TupleSections
TypeApplications TypeApplications
TypeFamilyDependencies TypeFamilyDependencies
TypeInType
TypeOperators TypeOperators
TypeSynonymInstances TypeSynonymInstances
ViewPatterns ViewPatterns

View File

@ -1,3 +1,4 @@
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-} {-# LANGUAGE UndecidableSuperClasses #-}
module Category.Constraint module Category.Constraint
( (:-) (Sub), (\\) ( (:-) (Sub), (\\)
@ -7,6 +8,7 @@ module Category.Constraint
import Category.Base import Category.Base
import Category.Functor import Category.Functor
import Category.Monoidal import Category.Monoidal
import Category.Product
import Data.Dict import Data.Dict
import Data.Kind (Constraint, Type) import Data.Kind (Constraint, Type)
@ -23,7 +25,7 @@ instance NiceCat (:-) where
id = Sub Dict id = Sub Dict
instance Functor (Nat (->) (:-)) (Yoneda (:-)) (:-) where instance Functor (Nat (->) (:-)) (Yoneda (:-)) (:-) where
map (Op (Sub f)) = Nat \_ (Sub g) -> Sub case f of Dict -> case g of Dict -> Dict map (Op (Sub f)) = Nat_ \(Sub g) -> Sub case f of Dict -> case g of Dict -> Dict
instance Functor (->) (:-) ((:-) a) where instance Functor (->) (:-) ((:-) a) where
map = (.) map = (.)
@ -31,6 +33,15 @@ instance Functor (->) (:-) ((:-) a) where
instance Functor (->) (:-) Dict where instance Functor (->) (:-) Dict where
map f = \Dict -> case f of Sub Dict -> Dict map f = \Dict -> case f of Sub Dict -> Dict
type UncurryC :: (a -> b -> Constraint) -> (a, b) -> Constraint
class f (Pi1 ab) (Pi2 ab) => UncurryC f ab
instance f (Pi1 ab) (Pi2 ab) => UncurryC f ab
type instance Uncurry = UncurryC
instance Unc (:-) where
uncurry _ = Sub Dict
ununcurry _ = Sub Dict
class (c, d) => ProdC c d class (c, d) => ProdC c d
instance (c, d) => ProdC c d instance (c, d) => ProdC c d
-- Note that, to my understanding, -- Note that, to my understanding,

View File

@ -3,8 +3,6 @@ module Category.Functor
( Functor, map ( Functor, map
, Endo, Endofunctor, endomap , Endo, Endofunctor, endomap
, Contravariant, contramap , Contravariant, contramap
, Bifunctor, bimap, first, second
, Profunctor, dimap, lmap, rmap
, Nat (Nat), runNat, pattern Nat_, natId , Nat (Nat), runNat, pattern Nat_, natId
, Const (Const), getConst , Const (Const), getConst
) where ) where
@ -35,28 +33,6 @@ instance {-# INCOHERENT #-} Functor dest (Yoneda src) f => Contravariant dest sr
instance {-# INCOHERENT #-} Functor dest src f => Contravariant dest (Yoneda src) f where instance {-# INCOHERENT #-} Functor dest src f => Contravariant dest (Yoneda src) f where
contramap (Op f) = map f contramap (Op f) = map f
type Bifunctor :: (j -> j -> Type) -> (i -> i -> Type) -> (i -> i -> j) -> Constraint
class (Functor (Nat dest src) src f, forall x. Functor dest src (f x)) => Bifunctor dest src f
instance (Functor (Nat dest src) src f, forall x. Functor dest src (f x)) => Bifunctor dest src f
bimap :: Bifunctor dest src f => src a c -> src b d -> dest (f a b) (f c d)
bimap f g = runNat (map f) (idR g) . map g
-- FIXME: A NiceCat dependency should not be necessary here,
-- this most likely means that my definition of Bifunctor is inadequate.
first :: forall dest src f a b c. (Bifunctor dest src f, NiceCat src) => src a b -> dest (f a c) (f b c)
first f = runNat (map f) (id :: Obj src c)
second :: Bifunctor dest src f => src b c -> dest (f a b) (f a c)
second g = map g
type Profunctor :: (j -> j -> Type) -> (i -> i -> Type) -> (i -> i -> j) -> Constraint
class (Functor (Nat dest src) (Yoneda src) f, forall x. Functor dest src (f x)) => Profunctor dest src f
instance (Functor (Nat dest src) (Yoneda src) f, forall x. Functor dest src (f x)) => Profunctor dest src f
dimap :: Profunctor dest src f => src a b -> src c d -> dest (f b c) (f a d)
dimap f g = runNat (map (Op f)) (idR g) . map g
lmap :: forall dest src f a b c. (Profunctor dest src f, NiceCat src) => src a b -> dest (f b c) (f a c)
lmap f = runNat (map (Op f)) (id :: Obj src c)
rmap :: Profunctor dest src f => src b c -> dest (f a b) (f a c)
rmap f = map f
type Nat :: (j -> j -> Type) -> (i -> i -> Type) -> (i -> j) -> (i -> j) -> Type type Nat :: (j -> j -> Type) -> (i -> i -> Type) -> (i -> j) -> (i -> j) -> Type
data Nat dest src f g = (Functor dest src f, Functor dest src g) => Nat { runNat :: !(forall a. Obj src a -> dest (f a) (g a)) } data Nat dest src f g = (Functor dest src f, Functor dest src g) => Nat { runNat :: !(forall a. Obj src a -> dest (f a) (g a)) }
@ -86,16 +62,16 @@ instance Functor (->) (FUN m) (FUN m a) where
map f = \g -> f . g map f = \g -> f . g
instance Functor (Nat (->) (FUN m)) (Yoneda (FUN m)) (FUN m) where instance Functor (Nat (->) (FUN m)) (Yoneda (FUN m)) (FUN m) where
map (Op f) = Nat \_ g -> g . f map (Op f) = Nat_ \g -> g . f
instance Functor (Nat (FUN m) (FUN m)) (FUN m) (,) where instance Functor (Nat (FUN m) (FUN m)) (FUN m) (,) where
map f = Nat \_ (x, y) -> (f x, y) map f = Nat_ \(x, y) -> (f x, y)
instance Functor (FUN m) (FUN m) ((,) a) where instance Functor (FUN m) (FUN m) ((,) a) where
map f = \(x, y) -> (x, f y) map f = \(x, y) -> (x, f y)
instance Functor (Nat (FUN m) (FUN m)) (FUN m) Either where instance Functor (Nat (FUN m) (FUN m)) (FUN m) Either where
map f = Nat \_ -> \case map f = Nat_ \case
Left y -> Left (f y) Left y -> Left (f y)
Right x -> Right x Right x -> Right x
@ -116,7 +92,7 @@ type Const :: Type -> i -> Type
newtype Const a b = Const { getConst :: a } newtype Const a b = Const { getConst :: a }
instance Functor (Nat (->) (->)) (->) Const where instance Functor (Nat (->) (->)) (->) Const where
map f = Nat \_ (Const x) -> Const (f x) map f = Nat_ \(Const x) -> Const (f x)
instance {-# INCOHERENT #-} Category src => Functor (->) src (Const a) where instance {-# INCOHERENT #-} Category src => Functor (->) src (Const a) where
map _ = \(Const x) -> Const x map _ = \(Const x) -> Const x

View File

@ -1,11 +1,13 @@
module Category.Monoidal module Category.Monoidal
( TensorProduct, Unit, unitObj, prodObj, prodIL, prodIR, prodEL, prodER, prodAL, prodAR ( TensorProduct, Unit, unitObj, prodObj, prodIL, prodIR, prodEL, prodER, prodAL, prodAR
, prodIL_, prodIR_, prodEL_, prodER_, prodAL_, prodAR_
, Compose (Compose), getCompose , Compose (Compose), getCompose
, Identity (Identity), getIdentity , Identity (Identity), getIdentity
) where ) where
import Category.Base import Category.Base
import Category.Functor import Category.Functor
import Category.Product
import Data.Either (Either (Left, Right)) import Data.Either (Either (Left, Right))
import Data.Kind (Constraint, FUN, Type) import Data.Kind (Constraint, FUN, Type)
import Data.Void (Void) import Data.Void (Void)
@ -15,7 +17,7 @@ import Data.Void (Void)
-- including the category of types itself, -- including the category of types itself,
-- so instead of using a @Monoidal@ typeclass, we use a @TensorProduct@ typeclass. -- so instead of using a @Monoidal@ typeclass, we use a @TensorProduct@ typeclass.
type TensorProduct :: (i -> i -> Type) -> (i -> i -> i) -> Constraint type TensorProduct :: (i -> i -> Type) -> (i -> i -> i) -> Constraint
class Endo Bifunctor morph prod => TensorProduct (morph :: i -> i -> Type) prod where class Bifunctor morph morph morph prod => TensorProduct (morph :: i -> i -> Type) prod where
type Unit morph prod :: i type Unit morph prod :: i
-- | The unit is an object. -- | The unit is an object.
unitObj :: proxy prod -> Obj morph (Unit morph prod) unitObj :: proxy prod -> Obj morph (Unit morph prod)
@ -40,6 +42,24 @@ class Endo Bifunctor morph prod => TensorProduct (morph :: i -> i -> Type) prod
-- | Reassociate a product, nesting it to the right. -- | Reassociate a product, nesting it to the right.
prodAR :: Obj morph a -> Obj morph b -> Obj morph c -> morph (prod (prod a b) c) (prod a (prod b c)) prodAR :: Obj morph a -> Obj morph b -> Obj morph c -> morph (prod (prod a b) c) (prod a (prod b c))
prodIL_ :: (NiceCat morph, TensorProduct morph prod) => morph a (prod a (Unit morph prod))
prodIL_ = prodIL id
prodIR_ :: (NiceCat morph, TensorProduct morph prod) => morph a (prod (Unit morph prod) a)
prodIR_ = prodIR id
prodEL_ :: (NiceCat morph, TensorProduct morph prod) => morph (prod a (Unit morph prod)) a
prodEL_ = prodEL id
prodER_ :: (NiceCat morph, TensorProduct morph prod) => morph (prod (Unit morph prod) a) a
prodER_ = prodER id
prodAL_ :: (NiceCat morph, TensorProduct morph prod) => morph (prod a (prod b c)) (prod (prod a b) c)
prodAL_ = prodAL id id id
prodAR_ :: (NiceCat morph, TensorProduct morph prod) => morph (prod (prod a b) c) (prod a (prod b c))
prodAR_ = prodAR id id id
instance TensorProduct (FUN m) (,) where instance TensorProduct (FUN m) (,) where
type Unit (FUN m) (,) = () type Unit (FUN m) (,) = ()
prodIL _ = \x -> (x, ()) prodIL _ = \x -> (x, ())
@ -49,20 +69,27 @@ instance TensorProduct (FUN m) (,) where
prodAL _ _ _ = \(x, (y, z)) -> ((x, y), z) prodAL _ _ _ = \(x, (y, z)) -> ((x, y), z)
prodAR _ _ _ = \((x, y), z) -> (x, (y, z)) prodAR _ _ _ = \((x, y), z) -> (x, (y, z))
absurd :: Void %1-> a
absurd = \case{}
instance TensorProduct (FUN m) Either where instance TensorProduct (FUN m) Either where
type Unit (FUN m) Either = Void type Unit (FUN m) Either = Void
prodIL _ = Left prodIL _ = Left
prodIR _ = Right prodIR _ = Right
prodEL _ (Left x) = x prodEL _ = \case
prodEL _ (Right x) = (\case{}) x Left x -> x
prodER _ (Left x) = (\case{}) x Right x -> absurd x
prodER _ (Right x) = x prodER _ = \case
prodAL _ _ _ (Left x) = Left (Left x) Left x -> absurd x
prodAL _ _ _ (Right (Left x)) = Left (Right x) Right x -> x
prodAL _ _ _ (Right (Right x)) = Right x prodAL _ _ _ = \case
prodAR _ _ _ (Left (Left x)) = Left x Left x -> Left (Left x)
prodAR _ _ _ (Left (Right x)) = Right (Left x) Right (Left x) -> Left (Right x)
prodAR _ _ _ (Right x) = Right (Right x) Right (Right x) -> Right x
prodAR _ _ _ = \case
Left (Left x) -> Left x
Left (Right x) -> Right (Left x)
Right x -> Right (Right x)
data Compose f g x = (Functor (->) (->) f, Functor (->) (->) g) => Compose { getCompose :: !(f (g x)) } data Compose f g x = (Functor (->) (->) f, Functor (->) (->) g) => Compose { getCompose :: !(f (g x)) }
newtype Identity x = Identity { getIdentity :: x } newtype Identity x = Identity { getIdentity :: x }
@ -71,10 +98,10 @@ instance Functor (FUN m) (FUN m) Identity where
map f = \(Identity x) -> Identity (f x) map f = \(Identity x) -> Identity (f x)
instance Functor (Nat (Nat (->) (->)) (Nat (->) (->))) (Nat (->) (->)) Compose where instance Functor (Nat (Nat (->) (->)) (Nat (->) (->))) (Nat (->) (->)) Compose where
map (Nat f) = Nat \(Nat _) -> Nat \_ (Compose x) -> Compose (f id x) map (Nat f) = Nat \(Nat _) -> Nat_ \(Compose x) -> Compose (f id x)
instance Functor (Nat (->) (->)) (Nat (->) (->)) (Compose f) where instance Functor (Nat (->) (->)) (Nat (->) (->)) (Compose f) where
map (Nat f) = Nat \_ (Compose x) -> Compose (map (f id) x) map (Nat f) = Nat_ \(Compose x) -> Compose (map (f id) x)
instance Functor (->) (->) (Compose (f :: Type -> Type) g) where instance Functor (->) (->) (Compose (f :: Type -> Type) g) where
map f = \(Compose x) -> Compose (map @_ @_ @_ @(->) (map f) x) map f = \(Compose x) -> Compose (map @_ @_ @_ @(->) (map f) x)

104
src/Category/Product.hs Normal file
View File

@ -0,0 +1,104 @@
{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses #-}
module Category.Product
( Pi1, Pi2, pairEta
, Product (Product)
, Uncurry, Uncurry' (Uncurry), getUncurry, UncurryN (UncurryN), getUncurryN
, Unc, uncurry, ununcurry
, Bifunctor, bimap_, bimap, first, second
, Profunctor, dimap
) where
import Category.Base
import Category.Functor
import Data.Dict
import Data.Kind (Constraint, FUN, Type)
import Data.Proxy
import Unsafe.Coerce (unsafeCoerce)
-- | The first projection of the type-level tuple.
type Pi1 :: (i, j) -> i
type family Pi1 xy where
Pi1 '(x, _) = x
-- | The second projection of the type-level tuple.
type Pi2 :: (i, j) -> j
type family Pi2 xy where
Pi2 '(_, y) = y
-- | Eta expansion for the pair type *on the type level*. **This does not hold on the value level.**
--
-- This is not provable by GHC's constraint solver, but it is safe to assume.
-- Maybe. Hopefully.
pairEta :: proxy x -> Dict (x ~ '(Pi1 x, Pi2 x))
pairEta = unsafeCoerce (Dict :: Dict ())
-- | The product category of two categories `c` and `d` is the category
-- whose objects are pairs of objects from `c` and `d` and whose arrows
-- are pairs of arrows from `c` and `d`.
type Product :: (i -> i -> Type) -> (j -> j -> Type) -> (i, j) -> (i, j) -> Type
data Product c d a b = Product !(c (Pi1 a) (Pi1 b)) !(d (Pi2 a) (Pi2 b))
instance (Category c, Category d) => Category (Product c d) where
idL (Product f g) = Product (idL f) (idL g)
idR (Product f g) = Product (idR f) (idR g)
Product f1 g1 . Product f2 g2 = Product (f1 . f2) (g1 . g2)
instance (NiceCat c, NiceCat d) => NiceCat (Product c d) where
id = Product id id
type Uncurry :: (a -> b -> c) -> (a, b) -> c
type family Uncurry
type Uncurry' :: (a -> b -> Type) -> (a, b) -> Type
newtype Uncurry' f ab = Uncurry { getUncurry :: f (Pi1 ab) (Pi2 ab) }
type instance Uncurry = Uncurry'
type UncurryN :: (a -> b -> c -> Type) -> (a, b) -> c -> Type
newtype UncurryN f ab x = UncurryN { getUncurryN :: f (Pi1 ab) (Pi2 ab) x }
type instance Uncurry = UncurryN
instance (Category c, Functor (->) c (f a b)) => Functor (->) c (UncurryN f '(a, b)) where
map f (UncurryN x) = UncurryN (map f x)
type Unc :: (c -> c -> Type) -> Constraint
class Category cat => Unc cat where
uncurry :: Obj cat (f a b) -> cat (f a b) (Uncurry f '(a, b))
ununcurry :: Obj cat (f a b) -> cat (Uncurry f '(a, b)) (f a b)
instance Unc (FUN m) where
uncurry _ = Uncurry
ununcurry _ (Uncurry x) = x
instance Unc (Nat (->) (->)) where
uncurry (Nat _) = Nat_ UncurryN
ununcurry (Nat _) = Nat_ \(UncurryN x) -> x
-- | A bifunctor is a functor whose domain is the product category.
type Bifunctor :: (k -> k -> Type) -> (i -> i -> Type) -> (j -> j -> Type) -> (i -> j -> k) -> Constraint
class (Unc cod, Category dom1, Category dom2, Functor cod (Product dom1 dom2) (Uncurry f)) => Bifunctor cod dom1 dom2 f where
bimap_ :: forall a b. Obj dom1 a -> Obj dom2 b -> Obj cod (f a b)
bimap :: forall cod dom1 dom2 f a b c d. Bifunctor cod dom1 dom2 f => dom1 a c -> dom2 b d -> cod (f a b) (f c d)
bimap f g = ununcurry (bimap_ (idR f) (idR g)) . map (Product f g) . uncurry (bimap_ (idL f) (idL g))
first :: forall cod dom f a b c. (NiceCat dom, Bifunctor cod dom dom f) => dom a b -> cod (f a c) (f b c)
first f = bimap f (id :: dom c c)
second :: forall cod dom f a b c. (NiceCat dom, Bifunctor cod dom dom f) => dom b c -> cod (f a b) (f a c)
second g = bimap (id :: dom a a) g
instance (Unc cod, Functor (Nat cod dom2) dom1 f, forall x. Functor cod dom2 (f x), uncurry ~ Uncurry) => Functor cod (Product dom1 dom2) (uncurry f) where
{-# INLINABLE map #-}
map :: forall a b. Product dom1 dom2 a b -> cod (uncurry f a) (uncurry f b)
map (Product f g) = lemma (uncurry (map (idR g)) . runNat (map f) (idR g) . map g . ununcurry (map (idL g)))
where lemma :: ((a ~ '(Pi1 a, Pi2 a), b ~ '(Pi1 b, Pi2 b)) => c) -> c
lemma x = case pairEta (Proxy @a) of Dict -> case pairEta (Proxy @b) of Dict -> x
instance (Unc cod, Category dom1, Functor (Nat cod dom2) dom1 f, forall x. Functor cod dom2 (f x)) => Bifunctor cod dom1 dom2 f where
bimap_ a b = runNat (map a) b . map b
type Profunctor :: (k -> k -> Type) -> (i -> i -> Type) -> (j -> j -> Type) -> (i -> j -> k) -> Constraint
class Bifunctor cod (Yoneda dom1) dom2 f => Profunctor cod dom1 dom2 f
instance Bifunctor cod (Yoneda dom1) dom2 f => Profunctor cod dom1 dom2 f
dimap :: Profunctor cod dom1 dom2 f => dom1 c a -> dom2 b d -> cod (f a b) (f c d)
dimap f g = bimap (Op f) g

View File

@ -10,7 +10,7 @@ class TensorProduct morph prod => Semigroup morph prod s where
append :: morph (prod s s) s append :: morph (prod s s) s
instance Semigroup (Nat (->) (->)) Compose Maybe where instance Semigroup (Nat (->) (->)) Compose Maybe where
append = Nat \_ (Compose x') -> case x' of append = Nat_ \(Compose x') -> case x' of
Nothing -> Nothing Nothing -> Nothing
Just Nothing -> Nothing Just Nothing -> Nothing
Just (Just x) -> Just x Just (Just x) -> Just x