Last active
October 23, 2023 20:10
-
-
Save sjoerdvisscher/95f3068551a94df4c4dbc9800d20d214 to your computer and use it in GitHub Desktop.
Universal properties with plain Control.Category
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE PolyKinds #-} | |
{-# LANGUAGE TypeFamilies #-} | |
{-# LANGUAGE TypeOperators #-} | |
{-# LANGUAGE FlexibleContexts #-} | |
{-# LANGUAGE MultiParamTypeClasses #-} | |
{-# LANGUAGE RankNTypes #-} | |
{-# LANGUAGE FlexibleInstances #-} | |
{-# LANGUAGE StandaloneKindSignatures #-} | |
{-# LANGUAGE GADTs #-} | |
{-# LANGUAGE UndecidableInstances #-} | |
import Control.Category | |
import Control.Comonad (Comonad(..)) | |
import Control.Comonad.Cofree | |
import Control.Monad.Free | |
import Prelude hiding (id, (.), Functor) | |
import qualified Prelude as P | |
import Data.Fix | |
import Data.Kind | |
import Data.Functor.Const | |
import Data.Functor.Kan.Lan | |
import Data.Functor.Kan.Ran | |
import Data.Functor.Rep | |
import Data.Void | |
type family Obj (c :: k -> k -> Type) (a :: k) :: Constraint | |
type instance Obj (->) a = () | |
class (Category (Dom f), Category (Cod f)) => Functor f where | |
type Dom f :: k1 -> k1 -> Type | |
type Cod f :: k2 -> k2 -> Type | |
map :: Dom f a b -> Cod f (f a) (f b) | |
class Functor r => InitUniv r a where | |
type L r a :: k | |
initUnivArr :: d ~ Cod r => d a (r (L r a)) | |
initUnivProp :: (c ~ Dom r, d ~ Cod r, Obj c b) => d a (r b) -> c (L r a) b | |
class Functor l => TermUniv l b where | |
type R l b :: k | |
termUnivArr :: c ~ Cod l => c (l (R l b)) b | |
termUnivProp :: (d ~ Dom l, c ~ Cod l, Obj d a) => c (l a) b -> d a (R l b) | |
data Unit a b where | |
Unit :: Unit a b | |
type instance Obj Unit a = () | |
instance Category Unit where | |
id = Unit | |
Unit . Unit = Unit | |
data Empty (c :: k -> k -> Type) (a :: k) = Empty | |
instance Category c => Functor (Empty c) where | |
type Dom (Empty c) = c | |
type Cod (Empty c) = Unit | |
map _ = Unit | |
instance InitUniv (Empty (->)) a where | |
type L (Empty (->)) a = Void | |
initUnivArr = Unit | |
initUnivProp Unit = absurd | |
instance TermUniv (Empty (->)) a where | |
type R (Empty (->)) a = () | |
termUnivArr = Unit | |
termUnivProp Unit _ = () | |
type family Fst ab | |
type instance Fst (a, b) = a | |
type family Snd ab | |
type instance Snd (a, b) = b | |
data (c :**: d) a b where | |
(:**:) :: c (Fst a) (Fst b) -> d (Snd a) (Snd b) -> (c :**: d) a b | |
type instance Obj (c :**: d) a = (Obj c (Fst a), Obj d (Snd a)) | |
instance (Category c, Category d) => Category (c :**: d) where | |
id = id :**: id | |
(l1 :**: l2) . (r1 :**: r2) = (l1 . r1) :**: (l2 . r2) | |
type instance Fst (Diag a) = a | |
type instance Snd (Diag a) = a | |
newtype Diag a = Diag { getDiag :: a } | |
instance Functor Diag where | |
type Dom Diag = (->) | |
type Cod Diag = (->) :**: (->) | |
map f = f :**: f | |
instance InitUniv Diag a where | |
type L Diag a = Either (Fst a) (Snd a) | |
initUnivArr = Left :**: Right | |
initUnivProp (l :**: r) = either l r | |
instance TermUniv Diag a where | |
type R Diag a = (Fst a, Snd a) | |
termUnivArr = fst :**: snd | |
termUnivProp (l :**: r) b = (l b, r b) | |
-- | Category of natural transformation with objects being an instance of class `c`. | |
newtype SubNat c f g = Nat { getNat :: forall a. f a -> g a } | |
type Nat = SubNat P.Functor | |
type instance Obj (SubNat c) f = c f | |
instance Category (SubNat c) where | |
id = Nat id | |
Nat m . Nat n = Nat (m . n) | |
instance Functor Const where | |
type Dom Const = (->) | |
type Cod Const = Nat | |
map f = Nat $ Const . f . getConst | |
newtype Forall f = Forall (forall a. f a) | |
instance TermUniv Const f where | |
type R Const f = Forall f | |
termUnivArr = Nat $ \(Const (Forall fa)) -> fa | |
termUnivProp (Nat n) a = Forall $ n (Const a) | |
data Exists f = forall a. Exists (f a) | |
instance InitUniv Const f where | |
type L Const f = Exists f | |
initUnivArr = Nat $ \fa -> Const (Exists fa) | |
initUnivProp (Nat n) (Exists fa) = getConst (n fa) | |
type Precompose :: (k1 -> Type) -> (Type -> Type) -> k1 -> Type | |
newtype Precompose f g a = Precompose { getPrecompose :: g (f a) } | |
instance Functor (Precompose f) where | |
type Dom (Precompose f) = Nat | |
type Cod (Precompose f) = Nat | |
map (Nat n) = Nat $ Precompose . n . getPrecompose | |
instance InitUniv (Precompose g) h where | |
type L (Precompose g) h = Lan g h | |
initUnivArr = Nat $ Precompose . glan | |
initUnivProp (Nat n) = Nat $ toLan (getPrecompose . n) | |
instance TermUniv (Precompose g) h where | |
type R (Precompose g) h = Ran g h | |
termUnivArr = Nat $ gran . getPrecompose | |
termUnivProp (Nat n) = Nat $ toRan (n . Precompose) | |
newtype SubHask c a b = Mor { getMor :: a -> b } | |
type instance Obj (SubHask c) a = c a | |
instance Category (SubHask c) where | |
id = Mor id | |
Mor f . Mor g = Mor (f . g) | |
newtype Forget (c :: Type -> Constraint) (a :: Type) = Forget { getForget :: a } | |
instance Functor (Forget c) where | |
type Dom (Forget c) = SubHask c | |
type Cod (Forget c) = (->) | |
map (Mor f) = Forget . f . getForget | |
instance InitUniv (Forget Monoid) a where | |
type L (Forget Monoid) a = [a] | |
initUnivArr = Forget . pure | |
initUnivProp f = Mor $ foldMap (getForget . f) | |
newtype FForget (c :: (Type -> Type) -> Constraint) (m :: Type -> Type) a = FForget { getFForget :: m a } | |
instance Functor (FForget c) where | |
type Dom (FForget c) = SubNat c | |
type Cod (FForget c) = Nat | |
map (Nat n) = Nat $ FForget . n . getFForget | |
instance P.Functor f => InitUniv (FForget Monad) f where | |
type L (FForget Monad) f = Free f | |
initUnivArr = Nat $ FForget . liftF | |
initUnivProp (Nat n) = Nat $ foldFree (getFForget . n) | |
-- The version from `free` requires `f` to be a `Comonad`, defeating the point! | |
lower :: P.Functor f => Cofree f b -> f b | |
lower (_ :< as) = fmap extract as | |
-- Missing from `free`. | |
unfoldCofree :: Comonad w => (forall x. w x -> f x) -> w a -> Cofree f a | |
unfoldCofree n w = extract w :< n (extend (unfoldCofree n) w) | |
instance P.Functor f => TermUniv (FForget Comonad) f where | |
type R (FForget Comonad) f = Cofree f | |
termUnivArr = Nat $ lower . getFForget | |
termUnivProp (Nat n) = Nat $ unfoldCofree (n . FForget) | |
instance P.Functor f => Functor (Co f) where | |
type Dom (Co f) = (->) | |
type Cod (Co f) = (->) | |
map = P.fmap | |
instance Representable f => InitUniv (Co f) () where | |
type L (Co f) () = Rep f | |
initUnivArr () = tabulate id | |
initUnivProp f = index (f ()) | |
class FAlg f a where | |
alg :: f a -> a | |
instance FAlg f (Fix f) where | |
alg = Fix | |
instance P.Functor f => InitUniv (Empty (SubHask (FAlg f))) a where | |
type L (Empty (SubHask (FAlg f))) a = Fix f | |
initUnivArr = Unit | |
initUnivProp Unit = Mor $ foldFix alg | |
class FCoalg f a where | |
coalg :: a -> f a | |
instance FCoalg f (Fix f) where | |
coalg = unFix | |
instance P.Functor f => TermUniv (Empty (SubHask (FCoalg f))) a where | |
type R (Empty (SubHask (FCoalg f))) a = Fix f | |
termUnivArr = Unit | |
termUnivProp Unit = Mor $ unfoldFix coalg |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment