proarrow-0.1.0.0: src/Proarrow/Category/Sheaf.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | Sites and sheaves.
--
-- A /cover/ of an object @a@ is a family of arrows into @a@, its /legs/, that together count as
-- all of @a@. The model is the opens of a space: an open is covered by smaller opens whose union
-- it is. A 'Site' is a category with a choice of covers, called a /coverage/.
--
-- Read an element of @p a b@ as data over @a@ (@b@ is a parameter along for the ride). Restricting
-- along a leg @g :: x '~>' a@, with @'lmap' g@, gives data over @x@. A family, one element per leg,
-- is /matching/ when its elements agree wherever two legs overlap: for legs @g@, @g'@ and any
-- @u :: z '~>' x@, @v :: z '~>' y@ with @'legArrow' g . u = 'legArrow' g' . v@,
-- @'lmap' u (m g) = 'lmap' v (m g')@. @p@ is a 'Sheaf' when every matching family is the
-- restriction of exactly one element at @a@. That rules out two failures:
--
-- * /Too many wholes./ Two distinct elements at @a@ restrict to the same family, so agreeing on
-- every leg does not make two things equal, and nothing can be proved by taking @a@ apart.
--
-- * /Too few./ A matching family is the restriction of no element at @a@, so compatible local
-- data cannot be assembled, and nothing can be built by putting @a@ together.
--
-- Neither implies the other, and counting the two sides decides neither:
--
-- @
-- elements at a matching families verdict
-- the representable at FLS, Atomic on BOOL 0 1 too few
-- the constant presheaf, Joins on (BOOL, BOOL) 2 1 too many
-- the collapsing presheaf, Atomic on BOOL 2 2 not injective
-- @
--
-- Overlap is stated over every commuting square, not over /the/ pullback, so two legs need not
-- have a pullback object. 'Sums' relies on this: it works over a free category that has none.
--
-- The arrows into @a@ that factor through some leg form the /sieve/ the cover generates. Covers are
-- given by their legs because a list of legs is finite and a sieve usually is not. Sieves are the
-- truth values of presheaf categories, see "Proarrow.Profunctor.Instance.Sieve". When covers are
-- stable and compose, the coverage generates a Grothendieck topology; 'Sums' shows it need not.
--
-- Covers given by generating arrows, and gluing as an operation rather than a condition, follow
-- Arnaud Spiwack's /Sheaves in Haskell/ (Tweag, 2026, <https://www.tweag.io/blog/2026-06-18-sheaves-in-haskell/>).
-- Added here: the category, the sieves, the classifier and a decision procedure for the sheaf
-- condition, the last three in "Proarrow.Category.Enriched.Finitary.Sheaf".
module Proarrow.Category.Sheaf where
import Data.Kind (Constraint, Type)
import Data.List (subsequences, tails)
import Data.Maybe (fromMaybe, listToMaybe)
import Prelude (Bool, Maybe (..), and, error, not, null, (||))
import Proarrow.Category.Enriched.Finitary (Finitary (..), FiniteCat, LocallyFinite, factorThrough, foreachOb)
import Proarrow.Category.Enriched.Thin (Thin)
import Proarrow.Category.Instance.Free (Elem, FREE)
import Proarrow.Category.Instance.Opposite (OPPOSITE (..))
import Proarrow.Category.Monoidal.Cartesian (Bicartesian)
import Proarrow.Colimit.BinaryCoproduct (HasBinaryCoproducts (..), type (+))
import Proarrow.Colimit.Initial (HasInitialObject (..))
import Proarrow.Core (CAT, CategoryOf (..), Hom, Kind, Profunctor (..), Promonad (..), obj, rmap, (//), type (+->))
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Pullback (HasPullbacks (..))
import Proarrow.Object (pattern Objs)
import Proarrow.Profunctor.Corepresentable (Corepresentable (..), withObCorep)
import Proarrow.Profunctor.Instance.Product (fstP, sndP, (:*:) (..))
import Proarrow.Profunctor.Instance.Rift (Rift (..))
import Proarrow.Profunctor.Instance.Terminal (TerminalProfunctor (..))
import Proarrow.Profunctor.Instance.Yoneda (Yo (..))
-- * Sites
-- | The kind of coverage names. A coverage is named by an empty type, the first argument of
-- 'Site', and has no values.
type Coverage :: Kind
type Coverage = Type
-- | A coverage, named @t@, on the category @k@. Several coverages can live on one category, so the
-- name is a parameter rather than a wrapper on the kind.
--
-- A cover is given by its /legs/, the arrows of the covering family. Every object is covered by
-- its identity; that cover is left implicit, and 'Cover' and 'covers' list the others. The laws are
--
-- [Stability] covers pull back: restricting a cover of @a@ to a part @b@ of @a@ gives a cover of
-- @b@. Given a cover @c@ of @a@ and any @f :: b '~>' a@, the object @b@ has a cover (possibly
-- just its identity) each of whose legs @h@ satisfies @f . h = 'legArrow' g . h'@ for some leg
-- @g@ of @c@ and some @h'@. Only the equation is asked for. No pullback object has to exist.
--
-- [Naming] a family over a cover @c@, a function @forall x. 'Leg' t k a c x -> r@ as 'glue' takes
-- and 'PulledBack' carries, is consulted only at the legs of @c@: those in @'legs' c@, or built
-- from the cover's own data. Its values at other legs are unspecified.
--
-- Naming matters when several covers share a type-level name @c@, as 'Atomic'\'s do on a category
-- that is not thin and 'Joins'\'s always do. Then 'Leg' also admits the legs of the other covers
-- with that name, so a hand-written 'glue' takes its legs from the cover it was given.
type Site :: Coverage -> Kind -> Constraint
class (CategoryOf k) => Site t k where
-- | A cover of @a@. The type @c@ names it, so that 'Leg' can say which cover a leg belongs to;
-- the value is the evidence that @c@ covers @a@.
data Cover t k (a :: k) (c :: Type)
-- | A leg of the cover @c@ of @a@, with source @x@.
data Leg t k (a :: k) (c :: Type) (x :: k)
-- Haddock gives no anchor to the constructors of a data family instance written inside a class
-- instance, so each coverage below names its own in the docs of its cover tags instead.
-- | The arrow a leg stands for.
legArrow :: Leg t k a c x -> x ~> a
-- | The legs of a cover.
legs :: Cover t k a c -> [SomeLeg t k a c]
-- | A leg of the cover @c@ of @a@, with its source hidden.
type SomeLeg :: Coverage -> forall (k :: Kind) -> k -> Type -> Type
data SomeLeg t k a c where
SomeLeg :: Leg t k a c x -> SomeLeg t k a c
-- | A site whose covers can be listed, object by object, as the law tests and the decision
-- procedure of "Proarrow.Category.Enriched.Finitary.Sheaf" need. A free category is a 'Site' but
-- not this: its 'Ob' cannot tell whether an object is a sum.
--
-- [Composition] covers compose. If @c@ covers @a@ and every leg of @c@ is itself covered, the
-- composites cover @a@ too. With Stability this makes the coverage generate a Grothendieck
-- topology, so 'Proarrow.Category.Enriched.Finitary.Sheaf.closure' is idempotent and preserves
-- meets, which 'Proarrow.Category.Enriched.Finitary.Sheaf.Plus' needs.
-- 'Proarrow.Testing.Laws.testLawvereTierney' is the check.
class (Site t k) => HasFiniteCovers t k where
-- | The covers of an object, beyond the identity.
covers :: forall (a :: k). (Ob a) => [SomeCover t k a]
-- | A cover of @a@, with its name hidden.
type SomeCover :: Coverage -> forall (k :: Kind) -> k -> Type
data SomeCover t k a where
SomeCover :: Cover t k a c -> SomeCover t k a
-- | How an arrow into @a@ factors through a cover of @a@: the leg it goes through, and the arrow
-- to that leg\'s source. The equation @f = 'legArrow' g . h@ is the caller\'s to rely on and the
-- instance\'s to respect.
type Factors :: Coverage -> forall (k :: Kind) -> k -> Type -> k -> Type
data Factors t k a c x where
Factors :: Leg t k a c y -> x ~> y -> Factors t k a c x
-- | How an arrow into @a@ factors through a cover of @a@, if it does: through the first leg it
-- factors through, found by 'factorThrough'. Only the hom-sets have to be finite.
factorThroughCover :: (Site t k, LocallyFinite k) => Cover t k a c -> x ~> a -> Maybe (Factors t k a c x)
factorThroughCover c h = listToMaybe [Factors l u | SomeLeg l <- legs c, Just u <- [h // legArrow l // factorThrough h (legArrow l)]]
-- | A cover pulled back along an arrow @f :: b '~>' a@: either @f@ itself factors through a leg
-- (the pullback is @b@\'s implicit identity cover), or some cover of @b@ has every leg factoring
-- through one.
type PulledBack :: Coverage -> forall (k :: Kind) -> k -> Type -> k -> Type
data PulledBack t k a c b where
AlreadyFactors :: Factors t k a c b -> PulledBack t k a c b
PulledBack :: Cover t k b c' -> (forall x. Leg t k b c' x -> Factors t k a c x) -> PulledBack t k a c b
-- | 'Site'\'s Stability law as an /operation/: a pullback one can compute with, carrying the
-- factorisation of each new leg through an old one. It lets a sheaf be glued structurally. Without
-- it, gluing into a carrier with no 'glue' of its own (an internal hom, the closed sieves) means
-- searching the carrier\'s elements ('Proarrow.Category.Enriched.Finitary.Topos.glueBySearch'),
-- which needs the carrier to be finitary.
--
-- Separate from 'Site' because 'Sums' cannot implement it: a free bicartesian category is not
-- extensive, so its covers do not pull back.
class (Site t k) => StableSite t k where
pullbackCover :: (Ob b) => Cover t k a c -> b ~> a -> PulledBack t k a c b
-- | A cover pulled back along the identity of the object it covers: itself, each leg factoring
-- through itself. Every instance needs this clause, and this version cannot get it wrong. In a
-- hand-written one, naming another leg with the same source type-checks.
pullbackAlongId :: (Site t k) => Cover t k a c -> PulledBack t k a c a
pullbackAlongId c = PulledBack c \l -> legArrow l // Factors l id
-- * Sheaves
-- | A profunctor that is a sheaf for the coverage @t@ on its contravariant side: every matching
-- family over a cover glues to one element. A family @m@ over the legs of a cover @c@ is
-- /matching/ when it agrees on overlaps: for legs @g@, @g'@ and any @u :: z '~>' x@,
-- @v :: z '~>' y@ with @'legArrow' g . u = 'legArrow' g' . v@, @'lmap' u (m g) = 'lmap' v (m g')@.
-- 'Sums' below is the smallest worked instance. The laws are
--
-- [Restriction] for matching @m@, @'lmap' ('legArrow' g) ('glue' c m) = m g@ at every leg @g@ of
-- @c@: the gluing restricts back to the family;
--
-- [Uniqueness] @'glue' c (\\g -> 'lmap' ('legArrow' g) x) = x@: an element is the gluing of its
-- own restrictions.
--
-- Together they make restriction a bijection from the elements at @a@ to the matching families on
-- @c@. On a non-matching family 'glue' is unspecified. The 'Sums' instance keeps one leg's
-- covariant component and drops the other, which is sound only because matching forces them equal.
--
-- @p@ is a sheaf when each presheaf @p (-) b@ is one, and 'glue' is stated for every @b@ at once.
-- A cosheaf, gluing on the covariant side, is a
-- @'Sheaf' t ('Proarrow.Category.Instance.Opposite.Op' p)@ for a coverage on @'OPPOSITE' k@. The
-- library defines no such coverage.
--
-- Instances are indexed by the shape of the profunctor: the limits below, a site's
-- representables, and the image of sheafification. An instance per coverage would overlap all of
-- them, so 'Trivial' gets the function 'glueTrivial' instead.
type Sheaf :: forall {j} {k}. Coverage -> j +-> k -> Constraint
class (Site t k, Profunctor p) => Sheaf t (p :: j +-> k) where
glue
:: forall (a :: k) c (b :: j)
. (Ob a, Ob b)
=> Cover t k a c
-> (forall x. Leg t k a c x -> p x b)
-> p a b
-- | The limits of profunctors are sheaves whenever their factors are: the terminal profunctor for
-- every coverage, and a product of sheaves glued componentwise.
instance (Site t k, CategoryOf j) => Sheaf t (TerminalProfunctor :: j +-> k) where
glue _ _ = TerminalProfunctor
instance (Sheaf t p, Sheaf t q) => Sheaf t (p :*: q) where
glue c m = glue @t c (\g -> fstP (m g)) :*: glue @t c (\g -> sndP (m g))
-- * Coverages
-- | The trivial coverage: only identities cover, so every profunctor is a sheaf.
type Trivial :: Coverage
type data Trivial
instance (CategoryOf k) => Site Trivial k where
data Cover Trivial k a c
data Leg Trivial k a c x
legArrow g = case g of {}
legs c = case c of {}
instance (CategoryOf k) => HasFiniteCovers Trivial k where
covers = []
instance (CategoryOf k) => StableSite Trivial k where
pullbackCover c _ = case c of {}
-- | Every profunctor is a sheaf for 'Trivial', by the eliminator of an empty 'Cover'. This is a
-- function rather than an @instance 'Sheaf' 'Trivial' p@ because that head and the two closure
-- instances above overlap (at @'Sheaf' 'Trivial' 'TerminalProfunctor'@, say) with neither more
-- specific than the other, so GHC could not choose between them. Write @glue = glueTrivial@ to get
-- the instance for one profunctor.
glueTrivial :: Cover Trivial k a c -> (forall x. Leg Trivial k a c x -> p x b) -> p a b
glueTrivial c _ = case c of {}
-- | The atomic coverage: every single arrow into an object covers it. So a sieve is covering iff it
-- is nonempty (the /atomic/ topology, with 'HasPullbacks' as its Ore condition). A sheaf is a
-- profunctor whose restriction along every arrow is a bijection. On a chain that is a presheaf
-- that is constant up to iso.
--
-- Stability is the pullback square: pulling a leg back along an arrow into its target gives the
-- cover of that arrow's source by the pullback projection, and the other projection factors it
-- through the old leg. Covers compose, since a composite of single arrows is a single arrow. This
-- is the first coverage here whose legs are themselves covered, so it is the first to exercise
-- 'HasFiniteCovers'\'s Composition law.
--
-- The identity is listed as a cover too. It decides nothing new, and dropping it would take
-- deciding @b ~ a@ under 'foreachOb', which a coverage generic in @k@ cannot do.
type Atomic :: Coverage
type data Atomic
-- | The name of the 'Atomic' cover of an object by a single arrow out of @b@: the 'Cover'
-- constructor is @Solely@ and its one 'Leg' constructor is @Only@. Two distinct arrows @b '~>' a@
-- share the name, so the name is not a singleton, and 'Site'\'s Naming law makes that
-- harmless: 'pullbackCover' answers for the leg of the cover it built, and says nothing true about
-- an @Only@ built from another arrow. On a thin category the arrow is unique and the name a
-- singleton after all.
type data Along (b :: k)
instance (HasPullbacks k, FiniteCat k) => Site Atomic k where
data Cover Atomic k a c where
Solely :: (Ob b) => b ~> a -> Cover Atomic k a (Along b)
data Leg Atomic k a c x where
Only :: (Ob b) => b ~> a -> Leg Atomic k a (Along b) b
legArrow (Only f) = f
legs (Solely f) = [SomeLeg (Only f)]
instance (HasPullbacks k, FiniteCat k) => HasFiniteCovers Atomic k where
covers @a = foreachOb @k \ @b -> [SomeCover (Solely f) | f <- elements @(Hom k) @b @a]
instance (HasPullbacks k, FiniteCat k) => StableSite Atomic k where
pullbackCover (Solely f) g = pullback f g \p1 p2 -> p1 // PulledBack (Solely p2) \(Only _) -> Factors (Only f) p1
-- | The open-cover coverage of a finite distributive lattice: an object is covered by any family of
-- objects below it whose join it is. Read the lattice as the opens of a finite space and its
-- sheaves are the sheaves on that space: a section over an open is determined by, and assembled
-- from, its sections over any opens that cover it. On @(BOOL, BOOL)@, the opens of the discrete
-- two-point space (see "Proarrow.Category.Instance.Product"), the whole space is covered by its
-- two points.
--
-- The empty family covers the bottom, the join of nothing, so a sheaf has exactly one section over
-- the bottom, as over the empty set. On a chain nothing else is covered.
--
-- 'covers' lists the antichains strictly below an object that join to it, found by enumerating
-- subsets, so it is for small lattices only. Other families generate the same sieves.
--
-- The instances ask for 'Proarrow.Category.Monoidal.Cartesian.Bicartesian' (meet is the product,
-- join the coproduct) and 'Thin', so that "is there an arrow" reads as @<=@.
-- 'Proarrow.Category.Instance.FinSet.FINSET' is bicartesian and distributive but not thin, and this
-- coverage would mean nothing there. Distributivity gives stability: pulled back along
-- @b '<=' a@, a cover @{x_i}@ of @a@ becomes @{b '&&' x_i}@, whose join is @b@. Covers compose,
-- since a join of joins is a join. The topology is subcanonical (representable presheaves are
-- sheaves), but a two-sided @'Yo' a ('OP' b)@ need not be one: the empty cover asks for one
-- element at the bottom for every object of @j@, and it has none at an object @b@ has no arrow to.
--
-- The finite, stable counterpart of 'Sums': a distributive lattice is the thin case of the
-- extensivity that a free bicartesian category lacks.
type Joins :: Coverage
type data Joins
-- | The name of every 'Joins' cover: the 'Cover' constructor is @ByJoin@, holding its legs, and
-- the 'Leg' constructor is @Under@, one per member of the family. A @ByJoin@ is a cover of @a@
-- only when its legs join to @a@; 'covers' lists exactly the antichains that do. All the covers
-- of an object share the name, so 'Site'\'s Naming law matters here: a family over
-- one of them is not asked about the legs of another.
type data Join
instance (Thin k, FiniteCat k, Bicartesian k) => Site Joins k where
data Cover Joins k a c where
ByJoin :: [SomeLeg Joins k a Join] -> Cover Joins k a Join
data Leg Joins k a c x where
Under :: (Ob x) => x ~> a -> Leg Joins k a Join x
legArrow (Under f) = f
legs (ByJoin ls) = ls
instance (Thin k, FiniteCat k, Bicartesian k) => HasFiniteCovers Joins k where
covers @a = [SomeCover (ByJoin ls) | ls <- subsequences strictlyBelow, isAntichain ls, isJoin ls]
where
strictlyBelow :: [SomeLeg Joins k a Join]
strictlyBelow = foreachOb @k \ @x -> [SomeLeg (Under f) | not (sourceBelow (obj @a) (obj @x)), f <- elements @(Hom k) @x @a]
isAntichain ls = and [not (legBelow l m || legBelow m l) | l : ms <- tails ls, m <- ms]
-- @a@ is below the join of the family, which is below @a@ by construction
isJoin ls = joinOf ls (sourceBelow (obj @a))
instance (Thin k, FiniteCat k, Bicartesian k) => StableSite Joins k where
pullbackCover c@(ByJoin ls) f = pullbackJoin c ls f
-- | Whether the source of the first arrow is below that of the second: in a thin category,
-- whether there is an arrow between them at all.
sourceBelow :: forall {k} (x :: k) (y :: k) a b. (FiniteCat k) => x ~> a -> y ~> b -> Bool
sourceBelow f g = f // g // not (null (elements @(Hom k) @x @y))
-- | Whether one leg's source is below the other's.
legBelow :: (FiniteCat k) => SomeLeg Joins k a Join -> SomeLeg Joins k a Join -> Bool
legBelow (SomeLeg (Under f)) (SomeLeg (Under g)) = sourceBelow f g
-- | The join of the legs' sources, as the arrow it has into their common target: the copairing
-- of the legs, starting from the initial object.
joinOf
:: forall {k} (a :: k) r
. (HasBinaryCoproducts k, HasInitialObject k, Ob a)
=> [SomeLeg Joins k a Join]
-> (forall j. j ~> a -> r)
-> r
joinOf [] kont = kont (initiate @k @a)
joinOf (SomeLeg (Under f) : ls) kont = joinOf ls (copair f)
where
copair :: forall x j. x ~> a -> j ~> a -> r
copair g h = g // h // withObCoprod @k @x @j (kont (g ||| h))
-- | 'Joins'\'s Stability: the meets of the source with the legs. Each is below its leg, so the
-- factorisation is found by 'factorThroughCover'; it is recomputed per leg rather than carried,
-- as a leg is only its arrow. The search fails only for a leg of some other cover of @b@, which
-- 'Site'\'s Naming law rules out.
pullbackJoin
:: forall {k} (b :: k) a
. (Thin k, FiniteCat k, Bicartesian k, Ob b)
=> Cover Joins k a Join
-> [SomeLeg Joins k a Join]
-> b ~> a
-> PulledBack Joins k a Join b
pullbackJoin c ls f = PulledBack (ByJoin [meet g | SomeLeg (Under g) <- ls]) \(Under h) ->
fromMaybe (error "pullbackJoin: not a leg of the pulled-back cover (Site's Naming law)") (factorThroughCover c (f . h))
where
meet :: forall x. (Ob x) => x ~> a -> SomeLeg Joins k b Join
meet _ = withObProd @k @b @x (SomeLeg (Under (fst @k @b @x)))
-- | The sum coverage on a free category with binary coproducts: a sum is covered by its two
-- injections. This is the syntactic site of Spiwack's post (see the module header). In the free
-- bicartesian closed category 'Proarrow.Tools.CCC.Syntax', the booleans @TermF '+' TermF@ are
-- covered by @true@ and @false@.
--
-- Read @p a@ as the ways of producing an @a@. Being a sheaf means @p Bool ≅ p TermF × p TermF@:
-- every pair of branches has a conditional, and only one. With too many, a proof by cases
-- establishes nothing. With too few, @if-then-else@ is not definable. On a representable the
-- conditional is @'|||'@, which is why 'glue' below is @[t, e]@. The conditional on a test
-- @f :: c '~>' Bool@ is 'Proarrow.Tools.CCC.either', which needs the distributive law as well.
--
-- __Stability does not hold.__ It would need every arrow into a sum to split its source into a
-- sum (/extensivity/), and a free bicartesian category is not extensive. The restriction of
-- @id '|||' 'lft' :: (u '+' u) '+' u ~> u '+' u@ to the left summand is @id@, which factors
-- through neither injection. With a richer constraint list,
-- @'Proarrow.Monoid.mempty' :: UnitF ~> u '+' u@ has a source that is not a sum at all.
-- So the coverage generates no Grothendieck topology. Nothing here relies on stability: 'glue'\'s
-- laws are the coproduct's universal property, and @FREE@ cannot list the covers of an arbitrary
-- object, so it is not a 'HasFiniteCovers' and the topology machinery never runs at it.
type Sums :: Coverage
type data Sums
-- | The name of the cover of @x '+' y@ by its injections, whose 'Cover' constructor is
-- @BySummands@ and whose 'Leg' constructors are @AtLeft@ and @AtRight@.
type Summands :: k -> k -> Type
type data Summands x y
instance (HasBinaryCoproducts `Elem` cs) => Site Sums (FREE cs (p :: CAT k)) where
data Cover Sums (FREE cs p) a c where
BySummands :: (Ob x, Ob y) => Cover Sums (FREE cs p) (x + y) (Summands x y)
data Leg Sums (FREE cs p) a c z where
AtLeft :: (Ob x, Ob y) => Leg Sums (FREE cs p) (x + y) (Summands x y) x
AtRight :: (Ob x, Ob y) => Leg Sums (FREE cs p) (x + y) (Summands x y) y
legArrow AtLeft = lft
legArrow AtRight = rgt
legs BySummands = [SomeLeg AtLeft, SomeLeg AtRight]
-- | Sums are colimits, so the representables are sheaves for 'Sums': gluing is @'|||'@ on the
-- contravariant component.
--
-- The initial object is needed too. An element of @'Yo' x ('OP' b)@ also has a covariant
-- component @b '~>' d@. A family over the two injections has one per leg, and the glued element
-- only one. Matching forces the two to agree because the injections overlap at the initial object,
-- @'lft' . initiate = 'rgt' . initiate@. Without it every family matches vacuously, and
-- restriction fails for any @j@ with a hom-set bigger than one.
instance
(HasBinaryCoproducts `Elem` cs, HasInitialObject `Elem` cs, CategoryOf j)
=> Sheaf Sums (Yo (x :: FREE cs (p :: CAT k)) (OP (b :: j)) :: j +-> FREE cs p)
where
glue BySummands m = case (m AtLeft, m AtRight) of
(Yo f h, Yo g _) -> Yo (f ||| g) h
-- | __The coverage by the image of a functor.__ A 'Corepresentable' @w@ is a functor
-- @F = w '%%' -@ from @k@ to @j@, with @w d c ≅ F d ~> c@. Every object @c@ of @j@ is covered by all
-- the arrows into it from the image, @F d ~> c@, so a leg is an element of @w@. For an object in the
-- image the cover contains its identity and asks nothing.
--
-- It is stable, since pulling a leg back along @g@ is composing with @g@, and the covers compose,
-- since the cover of a leg's source is again an image cover, and contains that source's identity.
-- A presheaf on @k@ extends to a sheaf on @j@: its right Kan lift @q '<|' w@ ('Rift'), whose value
-- at @c@ is a family over all the arrows @F d ~> c@. The restriction of a sheaf back to @k@ is
-- @w ':.:' s@, and the two are adjoint by the
-- 'Proarrow.Profunctor.Corepresentable.Corepresentable' instance of @'Star' ('Rift' ('OP' w))@.
--
-- The comparison lemma needs @F@ to be fully faithful: 'corepMap' is a bijection on each hom-set,
-- equivalently @('~>') ≅ w '|>' w@ ('Proarrow.Testing.Laws.testRanFullyFaithful'). Then the unit
-- of the adjunction is an isomorphism exactly on the sheaves, the counit is an isomorphism, and
-- the sheaves are the presheaves on @k@. Without it the coverage is still lawful, but its sheaves
-- are the presheaves on the full subcategory of @j@ on the objects @F d@. When @F@ sends two
-- objects to one, every presheaf is a sheaf, and the unit is a diagonal.
--
-- Two examples: the left inclusion of a collage, whose other objects are covered by the arrows
-- from the left layer, and the edges of a graph, covering each vertex by the two ends of an edge.
type ByImage :: forall {j} {k}. (j +-> k) -> Coverage
type data ByImage w
-- | The name of the one 'ByImage' cover of an object, whose 'Cover' constructor is @Images@ and
-- whose 'Leg' constructor is @FromImage@, one leg per element of @w@.
type data Image
instance (Corepresentable w, Finitary w, FiniteCat k) => Site (ByImage (w :: j +-> k)) j where
data Cover (ByImage w) j a c where
Images :: (Ob a) => Cover (ByImage w) j a Image
data Leg (ByImage w) j a c x where
FromImage :: (Ob d) => w d a -> Leg (ByImage w) j a Image (w %% d)
legArrow (FromImage x) = coindex x
legs @a Images = foreachOb @k \ @d -> [SomeLeg (FromImage x) | x <- elements @w @d @a]
instance (Corepresentable w, Finitary w, FiniteCat k) => HasFiniteCovers (ByImage (w :: j +-> k)) j where
covers = [SomeCover Images]
instance (Corepresentable w, Finitary w, FiniteCat k) => StableSite (ByImage (w :: j +-> k)) j where
pullbackCover Images g = PulledBack Images \(FromImage (x :: w d b)) -> withObCorep @w @d (Factors (FromImage (rmap g x)) id)
-- | The extension of a presheaf along @w@ is a sheaf: gluing reads each leg's family at the
-- identity of its source.
instance
(Corepresentable w, Finitary w, FiniteCat k, Profunctor q)
=> Sheaf (ByImage (w :: j +-> k)) (Rift (OP w) q :: i +-> j)
where
glue Images m = Rift \(x :: w d a) -> x // case m (FromImage x) of Rift f -> f (corepUniv @w @d)
-- | __The coverage induced along a functor.__ For a coverage @t@ on @j@ and a functor
-- @F = w '%%' -@ from @k@ to @j@, a cover of @d@ is a @t@-cover @c@ of @F d@, and its legs are
-- all the arrows @h :: e ~> d@ whose image @F h@ factors through a leg of @c@.
--
-- The comparison lemma: when @F@ is fully faithful and every object of @j@ is covered by arrows
-- out of the image ('Proarrow.Testing.Laws.testCoveredByImage'), the sheaves for @t@ are the
-- sheaves for @'Induced' t w@. Restriction is @w ':.:' s@ and extension is the
-- right Kan lift @q '<|' w@, glued by 'glueExtension', the adjunction of 'ByImage'. 'ByImage' is
-- the case where @t@ has only the image covers. Then every induced cover contains an identity,
-- and every presheaf on @k@ is a sheaf.
--
-- The opens of a space and a basis of it are the standard example: sheaves on the space are
-- sheaves on the basis.
type Induced :: forall {j} {k}. Coverage -> (j +-> k) -> Coverage
type data Induced t w
instance (Site t j, Corepresentable w, LocallyFinite j, FiniteCat k) => Site (Induced t (w :: j +-> k)) k where
data Cover (Induced t w) k d c where
Induce :: (Ob d) => Cover t j (w %% d) c -> Cover (Induced t w) k d c
data Leg (Induced t w) k d c e where
Induces :: (Ob e) => e ~> d -> Factors t j (w %% d) c (w %% e) -> Leg (Induced t w) k d c e
legArrow (Induces h _) = h
legs @d (Induce c) =
foreachOb @k \ @e ->
[SomeLeg (Induces h fs) | h <- elements @(Hom k) @e @d, Just fs <- [factorThroughCover c (corepMap @w h)]]
instance (HasFiniteCovers t j, Corepresentable w, LocallyFinite j, FiniteCat k) => HasFiniteCovers (Induced t (w :: j +-> k)) k where
covers @d = withObCorep @w @d [SomeCover (Induce c) | SomeCover c <- covers @t @j @(w %% d)]
-- | Pulling back along @h'@ is pulling the cover of @F d@ back along @F h'@.
instance (StableSite t j, Corepresentable w, LocallyFinite j, FiniteCat k) => StableSite (Induced t (w :: j +-> k)) k where
pullbackCover @d' (Induce c) h' =
withObCorep @w @d'
( case pullbackCover c (corepMap @w h') of
AlreadyFactors fs -> AlreadyFactors (Factors (Induces h' fs) id)
PulledBack c' fs -> PulledBack (Induce c') \(Induces h (Factors l' u)) -> case fs l' of
Factors l u' -> Factors (Induces (h' . h) (Factors l (u' . u))) id
)
-- | The extension of a sheaf for the induced coverage is a sheaf for @t@. Its value at @x :: F e ~> a@
-- is found by pulling the cover back along @x@ and gluing in @q@ over the induced cover of @e@.
--
-- A function instead of an instance, because an instance would overlap the 'ByImage' one.
glueExtension
:: forall t {i} {j} {k} (w :: j +-> k) (q :: i +-> k) (a :: j) c (b :: i)
. (StableSite t j, Corepresentable w, LocallyFinite j, FiniteCat k, Sheaf (Induced t w) q, Ob a, Ob b)
=> Cover t j a c
-> (forall x. Leg t j a c x -> Rift (OP w) q x b)
-> Rift (OP w) q a b
glueExtension c m = Rift \(x@Objs :: w e a) ->
withObCorep @w @e
( case pullbackCover c (coindex x) of
AlreadyFactors (Factors l u) -> at l (rmap u (corepUniv @w @e))
PulledBack c' fs -> glue @(Induced t w) (Induce c') \(Induces _ (Factors l' u)) -> case fs l' of
Factors l u' -> at l (rmap (u' . u) corepUniv)
)
where
at :: Leg t j a c y -> w e' y -> q e' b
at l y = case m l of Rift f -> f y