| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Proarrow.Category.Sheaf
Contents
Description
Sites and sheaves. A Site is a category with a coverage: for each object, the families of
arrows into it that count as covering it, each given by its legs. A profunctor is a Sheaf
for the coverage when, on every cover, every matching family of its elements glues to exactly one
element at the covered object.
A cover of a generates a sieve: every arrow into a that factors through one of the cover's
legs. When the covers are stable and compose, the coverage generates a Grothendieck topology;
Sums below shows that it need not. Covers are given by their legs here because that is what
someone writing a site down actually has -- a sieve is usually infinite, a list of legs is not.
Those sieves are also the truth values of a category of presheaves; see
Proarrow.Profunctor.Instance.Sieve.
Asking for a sheaf rather than a bare profunctor rules out two specific failures. Say you want
p a to be data local to a, and a cover of a to be a way of taking that data apart. A
bare profunctor says nothing about how the whole relates to its parts, so it allows both of
these:
- Too many wholes. Two distinct elements at
acan restrict to the same family on the cover. Then agreeing everywhere on a cover does not make two things equal, so nothing can be proved by taking an object apart. - Too few. A family on the cover that agrees on the overlaps can have no element at
arestricting to it. Then compatible local data cannot be assembled, so nothing can be built by putting an object together.
The sheaf condition is exactly the absence of both -- see Sheaf for the laws. Neither half
implies the other, and counting the two sides decides neither, as three presheaves from
Props.Sheaf show:
elements at a matching families verdict the representable at FLS, ByArrow 0 1 too few the constant presheaf, Canonical 2 1 too many the collapsing presheaf, ByArrow 2 2 not injective
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/).
What is added here is the category. Matching is stated as an equation over commuting squares: for
any z and any p :: z , ~> xq :: z with ~> y,
legArrow g . p = legArrow g' . q. It quantifies over such lmap p (m g) = lmap q (m g')z rather than over the pullback,
so the pullback of two legs need not exist as an object -- which is what lets Sums work over a
free category that has none. Added too, in Proarrow.Category.Enriched.Finitary.Sheaf: the
sieves, the classifier and a decision procedure for the sheaf condition.
Synopsis
- class CategoryOf k => Site t k where
- data SomeLeg t k (a :: k) c where
- class Site t k => HasFiniteCovers t k where
- data SomeCover t k (a :: k) where
- class (Site t k, Profunctor p) => Sheaf t (p :: j +-> k) where
- data Trivial
- glueTrivial :: forall {k1} k2 (a :: k2) c p (b :: k1). Cover Trivial k2 a c -> (forall (x :: k2). Leg Trivial k2 a c x -> p x b) -> p a b
- data ByArrow
- data TruByFls
- data Canonical
- data ByPoints
- data ByNothing
- data Sums
- data Summands (x :: k) (y :: k)
Sites
class CategoryOf k => Site t k where Source Github #
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 generating arrows of the covering family. The identity cover,
which every coverage has, is left implicit: Cover and covers list the others. The one law is
- Stability
- covers pull back. Given a cover
cofaand anyf :: b, the object~>abhas a cover -- possibly just its identity -- each of whose legshsatisfiesf . h =for some leglegArrowg . h'gofcand someh'. Only the equation is asked for; no pullback object has to exist.
Associated Types
data Cover t k (a :: k) c Source Github #
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 Leg t k (a :: k) c (x :: k) Source Github #
A leg of the cover c of a, with source x.
Methods
legArrow :: forall (a :: k) c (x :: k). Leg t k a c x -> x ~> a Source Github #
The arrow a leg stands for.
legs :: forall (a :: k) c. Cover t k a c -> [SomeLeg t k a c] Source Github #
The legs of a cover.
Instances
| Site ByArrow BOOL Source Github # | |
| CategoryOf k => Site Trivial k Source Github # | |
| Site Canonical (BOOL, BOOL) Source Github # | |
| Elem HasBinaryCoproducts cs => Site Sums (FREE cs p) Source Github # | |
class Site t k => HasFiniteCovers t k where Source Github #
A site whose covers can be listed, object by object. This is what the law tests and the
decision procedure of Proarrow.Category.Enriched.Finitary.Sheaf quantify over. A free category
is a Site but not this: whether an object is a sum is not something its Ob can answer.
Beyond Site's Stability, listing the covers comes with a second law:
- Composition
- covers compose. If
ccoversaand every leg ofcis itself covered, the composites coveratoo. Stability and composition together are what make the coverage generate a Grothendieck topology, and so makeclosureidempotent and meet-preserving -- without which the dense sieves at an object are not closed under meets, which is whatPlusrests on.testLawvereTierneyis the check.
Methods
covers :: forall (a :: k). Ob a => [SomeCover t k a] Source Github #
The covers of an object, beyond the identity.
Instances
Sheaves
class (Site t k, Profunctor p) => Sheaf t (p :: j +-> k) where Source Github #
A profunctor that is a sheaf for the coverage t on the contravariant side: elements over a
cover glue. A family m over the legs of a cover c is matching when it agrees on
overlaps: for legs g and g' and any p :: z , ~> xq :: z with
~> y, legArrow g . p = legArrow g' . q. lmap p (m g) = lmap q (m g')Sums below is the
smallest worked instance of that condition, where it comes to one equation. The laws are
- Restriction
- for matching
m,at every leglmap(legArrowg) (gluec m) = m ggofc: the gluing restricts back to the family; - Uniqueness
: an element is the gluing of its own restrictions.gluec (\g ->lmap(legArrowg) x) = x
The laws constrain glue on matching families only, and its type accepts any family, so its
value on a non-matching one is unspecified. The Sums instance below is the live example: it
keeps one leg's covariant component and discards the other, which is sound only because matching
forces them equal.
Together they say that restriction is a bijection from the elements at a to the matching
families on c, which is the sheaf condition. Gluing is stated for every b at once: a
profunctor j is a sheaf when, for each object +-> kb of j, the presheaf p (-) b on k
is one. Once the glued element at (a, b) is fixed, dimap fixes its value at every other point
(g, h) of the sieve the cover generates -- which is why
Proarrow.Category.Enriched.Finitary.Sheaf can decide the same condition as natural
transformations out of that sieve.
Gluing is contravariant, and not by choice: a coverage presents each object by the arrows into
it, and k is the side of p a b that lmap acts on. The dual needs a
cocoverage, which is a coverage on , and a cosheaf for it is
OPPOSITE k; nothing supplies a cocoverage yet,
though Sheaf t (Op p)
makes the machinery available for one.
The instances are indexed by the shape of the profunctor: the limits just below, a site's
representables, and the image of sheafification
(Finitary (Op p)Plus twice over). One indexed by the coverage
instead cuts across that axis and so overlaps all of them -- which is why glueTrivial is a
function and not an instance .Sheaf Trivial p
Methods
glue :: forall (a :: k) c (b :: j). (Ob a, Ob b) => Cover t k a c -> (forall (x :: k). Leg t k a c x -> p x b) -> p a b Source Github #
Instances
| (Site t k, CategoryOf j) => Sheaf t (TerminalProfunctor :: k -> j -> Type) Source Github # | The limits of profunctors are sheaves whenever their factors are: the terminal profunctor for every coverage, and a product of sheaves glued componentwise. |
Defined in Proarrow.Category.Sheaf Methods glue :: forall (a :: k) c (b :: j). (Ob a, Ob b) => Cover t k a c -> (forall (x :: k). Leg t k a c x -> TerminalProfunctor x b) -> TerminalProfunctor a b Source Github # | |
| (Site t k, LocallyFinite k, CategoryOf j) => Sheaf t (Sheafify t p :: k -> j -> Type) Source Github # | Sheafification lands in the sheaves. This is the theorem
|
| (Sheaf t p, Sheaf t q) => Sheaf t (p :*: q :: k -> j -> Type) Source Github # | |
| Sheaf t p => Sheaf t (Reindex 'Subobject p fs :: k -> j -> Type) Source Github # | A retabulation of a sheaf glues as the sheaf does -- for a A |
| (Site t k, FiniteCat j, FiniteCat k, KnownTables j k lm rm) => Sheaf t (Tabulated t lm rm :: k -> j -> Type) Source Github # | A tabulated profunctor glues by search, since it knows nothing of the profunctor it presents.
Only for the coverage its tag names: the tag is the whole of the evidence that the search will
find its element, so a presentation may be used as a sheaf for the coverage it was checked
against and for no other. At |
| (Elem HasBinaryCoproducts cs, Elem HasInitialObject cs, CategoryOf j) => Sheaf Sums (Yo x ('OP b) :: FREE cs p -> j -> Type) Source Github # | Sums are colimits, so the representables are sheaves for The initial object is needed, even though the coproduct alone builds the gluing. An element of
The decision procedure in Proarrow.Category.Enriched.Finitary.Sheaf does not need this,
because it reads the condition off the generated sieve, which carries every arrow of |
Example sites
The trivial coverage: only identities cover, so every profunctor is a sheaf.
Instances
| CategoryOf k => HasFiniteCovers Trivial k Source Github # | |
| CategoryOf k => Site Trivial k Source Github # | |
| data Cover Trivial k (a :: k) c Source Github # | |
Defined in Proarrow.Category.Sheaf | |
| data Leg Trivial k (a :: k) c (x :: k) Source Github # | |
Defined in Proarrow.Category.Sheaf | |
glueTrivial :: forall {k1} k2 (a :: k2) c p (b :: k1). Cover Trivial k2 a c -> (forall (x :: k2). Leg Trivial k2 a c x -> p x b) -> p a b Source Github #
Every profunctor is a sheaf for Trivial, by the eliminator of an empty Cover. This is a
function rather than an instance because that head and the two closure
instances above overlap -- at Sheaf Trivial p, say -- with neither more
specific than the other, so GHC could not choose between them. Write Sheaf Trivial TerminalProfunctorglue = glueTrivial to get
the instance for one profunctor.
The walking arrow BOOL with TRU covered by FLS alone. The one leg has no overlap
with itself beyond its source, so every element at FLS is a matching family, and a presheaf is
a sheaf exactly when its restriction along F2T is a bijection. The coverage is stable, since
pulling the cover back along F2T gives the identity of FLS, but not subcanonical: the
representable at FLS has an element at FLS and none at TRU, so it is not a sheaf.
Instances
| HasFiniteCovers ByArrow BOOL Source Github # | |
| Site ByArrow BOOL Source Github # | |
| data Cover ByArrow BOOL (a :: BOOL) c Source Github # | |
| data Leg ByArrow BOOL (a :: BOOL) c (x :: BOOL) Source Github # | |
data Canonical Source Github #
The open-cover coverage of the discrete two-point space, whose opens are (BOOL, BOOL) (see
Proarrow.Category.Instance.Product): the whole space is covered by its two singletons, and the
empty set by no opens at all. The covers are stable and compose, so the covering sieves are
exactly those whose union is the object; and since a union of opens is their colimit -- the
empty union included -- every representable presheaf is a sheaf: the coverage is subcanonical.
A sheaf has exactly one section over the empty set, and its sections over the whole space are
the pairs of sections over the two points.
Subcanonical is about the representable presheaves. A two-sided need not be
a sheaf: the empty cover asks for exactly one element at Yo a (OP b)'(FLS, FLS) for every object of j,
while has none at an object Yo a (OP b)b has no arrow to.
Instances
| HasFiniteCovers Canonical (BOOL, BOOL) Source Github # | |
| Site Canonical (BOOL, BOOL) Source Github # | |
| data Cover Canonical (BOOL, BOOL) (a :: (BOOL, BOOL)) c Source Github # | |
| data Leg Canonical (BOOL, BOOL) (a :: (BOOL, BOOL)) c (x :: (BOOL, BOOL)) Source Github # | |
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) -- the free
bicartesian closed category Syntax is one such category, with the booleans
the sum TermF covered by + TermFtrue and false.
Read p a as the ways of producing a result of type a, and the module header's two failures
become concrete: too many is two elements of p Bool with the same branches, so a proof by
cases establishes nothing; too few is a pair of branches with no conditional, so if-then-else
is not definable.
One caveat, and a real one: Site's Stability law does not hold for this coverage as
declared. Covers live only at objects whose shape is syntactically x , so stability would
need every arrow into a sum to decompose its domain -- that is extensivity, and a free
bicartesian category is not extensive. Two witnesses: + yid at
||| lft(u needs only coproducts, and its restriction to the left summand is
+ u) + u ~> u + uid, which factors through neither injection; and with a richer constraint list
has a source that is not a sum at all, so no
cover is even available. Nothing here breaks -- mempty :: UnitF ~> u + uglue's laws below are the coproduct's
universal property and need no stability, and FREE is deliberately not a HasFiniteCovers, so the
topology machinery is never instantiated at it -- but this coverage does not generate a
Grothendieck topology, and the licence that HasFiniteCovers's covers gives -- to check only
the covers it lists -- does not extend to it.
Being a sheaf is exactly p Bool ≅ p TermF × p TermF: every pair of branches has a
conditional, and only one. On a representable that conditional is , which is why |||glue
below is [t, e]; the conditional on a test f :: c with branches over ~> Boolc is
either, which needs the distributive law as well.
The free category cannot list the covers of an arbitrary object, so this is a Site and not a
HasFiniteCovers.
Instances
| Elem HasBinaryCoproducts cs => Site Sums (FREE cs p) Source Github # | |
| (Elem HasBinaryCoproducts cs, Elem HasInitialObject cs, CategoryOf j) => Sheaf Sums (Yo x ('OP b) :: FREE cs p -> j -> Type) Source Github # | Sums are colimits, so the representables are sheaves for The initial object is needed, even though the coproduct alone builds the gluing. An element of
The decision procedure in Proarrow.Category.Enriched.Finitary.Sheaf does not need this,
because it reads the condition off the generated sieve, which carries every arrow of |
| data Cover Sums (FREE cs p) (a :: FREE cs p) c Source Github # | |
| data Leg Sums (FREE cs p) (a :: FREE cs p) c (z :: FREE cs p) Source Github # | |
Defined in Proarrow.Category.Sheaf data Leg Sums (FREE cs p) (a :: FREE cs p) c (z :: FREE cs p) where
| |