Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ New modules

* `Data.Sign.Show` to show a sign

* Added a new domain theory section to the library under `Relation.Binary.Domain.*`:
- Introduced new modules and bundles for domain theory, including `DirectedCompletePartialOrder`, `Lub`, and `ScottContinuous`.
- All files for domain theory are now available in `src/Relation/Binary/Domain/`.

Additions to existing modules
-----------------------------

Expand Down
5 changes: 5 additions & 0 deletions src/Relation/Binary/Definitions.agda
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ Min R = Max (flip R)
Minimum : Rel A ℓ → A → Set _
Minimum = Min

-- Directed families

SemiDirected : Rel A ℓ → (B → A) → Set _
SemiDirected _≤_ f = ∀ i j → ∃[ k ] (f i ≤ f k × f j ≤ f k)
Comment on lines +144 to +145
Copy link
Contributor

@jamesmckinna jamesmckinna Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... after thinking about this for quite a bit, would it make (better) sense to factorise this definition so that

  • SemiDirected refers only to the underlying relation, as: ∀ (x y : A) → ∃[ z ] (x ≤ z × y ≤ z)
  • given _≤_ : Rel A ℓ, and f : B → A we obtain an ordering _≤[ f ]_ on B by i ≤[ f ] k = f i ≤ f k (given by _≤_ on f using Function.Base._on_)
  • then the above definition is definitionally equal to SemiDirected _≤[ f ]_

UPDATED: see #2813


-- Definitions for apartness relations

-- Note that Cotransitive's arguments are permuted with respect to Transitive's.
Expand Down
16 changes: 16 additions & 0 deletions src/Relation/Binary/Domain.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
------------------------------------------------------------------------
-- The Agda standard library
--
-- Order-theoretic Domains
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Relation.Binary.Domain where

------------------------------------------------------------------------
-- Re-export various components of the Domain hierarchy

open import Relation.Binary.Domain.Definitions public
open import Relation.Binary.Domain.Structures public
open import Relation.Binary.Domain.Bundles public
66 changes: 66 additions & 0 deletions src/Relation/Binary/Domain/Bundles.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
------------------------------------------------------------------------
-- The Agda standard library
--
-- Bundles for domain theory
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Relation.Binary.Domain.Bundles where

open import Level using (Level; _⊔_; suc)
open import Relation.Binary.Bundles using (Poset)
open import Relation.Binary.Structures using (IsDirectedFamily)
open import Relation.Binary.Domain.Structures
using (IsDirectedCompletePartialOrder; IsScottContinuous
; IsLub)

private
variable
o ℓ e o' ℓ' e' ℓ₂ : Level
Ix A B : Set o

------------------------------------------------------------------------
-- Directed Complete Partial Orders
------------------------------------------------------------------------

record DirectedFamily {c ℓ₁ ℓ₂ : Level} {P : Poset c ℓ₁ ℓ₂} {B : Set c} : Set (c ⊔ ℓ₁ ⊔ ℓ₂) where
field
f : B → Poset.Carrier P
isDirectedFamily : IsDirectedFamily (Poset._≈_ P) (Poset._≤_ P) f

open IsDirectedFamily isDirectedFamily public

record DirectedCompletePartialOrder (c ℓ₁ ℓ₂ : Level) : Set (suc (c ⊔ ℓ₁ ⊔ ℓ₂)) where
field
poset : Poset c ℓ₁ ℓ₂
isDirectedCompletePartialOrder : IsDirectedCompletePartialOrder poset

open Poset poset public
open IsDirectedCompletePartialOrder isDirectedCompletePartialOrder public

------------------------------------------------------------------------
-- Scott-continuous functions
------------------------------------------------------------------------

record ScottContinuous
{c₁ ℓ₁₁ ℓ₁₂ c₂ ℓ₂₁ ℓ₂₂ : Level}
(P : Poset c₁ ℓ₁₁ ℓ₁₂)
(Q : Poset c₂ ℓ₂₁ ℓ₂₂)
(κ : Level) : Set (suc (κ ⊔ c₁ ⊔ ℓ₁₁ ⊔ ℓ₁₂ ⊔ c₂ ⊔ ℓ₂₁ ⊔ ℓ₂₂)) where
field
f : Poset.Carrier P → Poset.Carrier Q
isScottContinuous : IsScottContinuous P Q f κ

open IsScottContinuous isScottContinuous public

------------------------------------------------------------------------
-- Lubs
------------------------------------------------------------------------

record Lub {c ℓ₁ ℓ₂ : Level} {P : Poset c ℓ₁ ℓ₂} {B : Set c}
(f : B → Poset.Carrier P) : Set (c ⊔ ℓ₁ ⊔ ℓ₂) where
open Poset P
field
lub : Carrier
isLub : IsLub P f lub
31 changes: 31 additions & 0 deletions src/Relation/Binary/Domain/Definitions.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
------------------------------------------------------------------------
-- The Agda standard library
--
-- Definitions for domain theory
------------------------------------------------------------------------




{-# OPTIONS --cubical-compatible --safe #-}

module Relation.Binary.Domain.Definitions where

open import Data.Product using (∃-syntax; _×_; _,_)
open import Function using (_∘_)
open import Level using (Level; _⊔_; suc)
open import Relation.Binary.Core using (Rel)

private
variable
a b i ℓ ℓ₁ ℓ₂ : Level
A : Set a
B : Set b
I : Set ℓ

------------------------------------------------------------------------
-- Upper bound
------------------------------------------------------------------------

UpperBound : {A : Set a} → Rel A ℓ → {B : Set b} → (f : B → A) → A → Set _
UpperBound _≤_ f x = ∀ i → f i ≤ x
64 changes: 64 additions & 0 deletions src/Relation/Binary/Domain/Structures.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
------------------------------------------------------------------------
-- The Agda standard library
--
-- Structures for domain theory
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Relation.Binary.Domain.Structures where

open import Data.Product using (_×_; _,_; proj₁; proj₂)
open import Function using (_∘_)
open import Level using (Level; _⊔_; suc)
open import Relation.Binary.Bundles using (Poset)
open import Relation.Binary.Structures using (IsDirectedFamily)
open import Relation.Binary.Domain.Definitions using (UpperBound)
open import Relation.Binary.Morphism.Structures using (IsOrderHomomorphism)

private variable
a b c c₁ c₂ ℓ ℓ₁ ℓ₂ ℓ₁₁ ℓ₁₂ ℓ₂₁ ℓ₂₂ : Level
A B : Set a


module _ (P : Poset c ℓ₁ ℓ₂) where
open Poset P
Comment on lines +24 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this has come up before in this context; we tend to define this kind of parametrised record on the underlying Raw structure (because none of the axioms for a Poset are involved in the definition, only the underlying relations).

Not least because domain theory ought better to be built on Preorder-based foundations? Antisymmetry plays almost no direct role in the axiomatisation of the concepts you define?

Anyway: meta design principle is/ought to be 'structures don't depend on bundles'

I think (but happy to be contradicted) that UpperBound, Lub, DirectedFamily all belong in Relation.Binary.Domain.Definitions

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can blame this on me 😁

In setoid-based foundations, it doesn't really make sense to found order theory on preorders. To see why, let's look at a very relevant example: uniqueness of lubs. In a poset (P, _≈_, _≤_), if IsLub f x and IsLub f y, then we get x ≈ y. Conversely, if P is a preorder, the best we can do is (x ≤ y) × (y ≤ x). This looks something like the following:

module PosetAPI (P : Poset) where
  open Poset P
  
  Lub-unique :  {Ix : Set} {f : Ix  Carrier} {x y}  IsLub f x  IsLub f y  x ≈ y
  Lub-unique = ...

module PreorderAPI (P : Preorder) where
  open Preorder P
  
  Lub-unique :  {Ix : Set} {f : Ix  Carrier} {x y}  IsLub f x  IsLub f y  (x ≤ y) × (y ≤ x)
  Lub-unique = ...

However, we can complete any preorder P to a poset PosetalCompletion P by replacing the equivalence relation on P with (x ≤ y × y ≤ x). If we instantiate the PosetAPI with PosetalCompletion P, we will get
exactly the theorem in PreorderAPI, so you don't lose any generality by just considering posets. In fact, you get a more general API: PreorderAPI is just PosetAPI with a particular choice of equivalence relation 🙂 .

As for parameterising by raw structures, my comments about poor inference from the last PR are still relevant. I don't really see anyone considering least-upper bounds in a raw structure, so the tradeoff of generality for inference performance doesn't seem worth it here. There is a lot of precedence for this choice in agda-categories: for instance, we don't ever talk about products in a raw category.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented on posetal completion on #2721 , sorry for that.

As for parametrisation by a Poset, also see my previous remarks about this form of 'structure on bundle' dependency being not the prevailing design heuristic of stdlib. As to what 'really' happens/should, and justification-by-precedent for alternative design philosophies/policies from other agda libraries or elsewhere, that's a bigger question than can be solved in one PR, so suggest it be opened as a library-design/discussion issue...


record IsLub {b : Level} {B : Set b} (f : B Carrier)
Copy link
Contributor

@jamesmckinna jamesmckinna Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b is already implicitly bound by a variable declaration (as is B!), so shouldn't need explicit mention here.

(lub : Carrier) : Set (b ⊔ c ⊔ ℓ₁ ⊔ ℓ₂) where
field
isUpperBound : UpperBound _≤_ f lub
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase : CamelCase is the usual convention in stdlib, accordingly:

Suggested change
isUpperBound : UpperBound _≤_ f lub
upperBound : UpperBound _≤_ f lub

isLeast : y UpperBound _≤_ f y lub ≤ y
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isLeast : y UpperBound _≤_ f y lub ≤ y
minimal : y UpperBound _≤_ f y lub ≤ y

because being least only follows from the Poset axioms, whereas in their absence (as above), we only have/need a minimal upper bound?


record IsDirectedCompletePartialOrder : Set (suc (c ⊔ ℓ₁ ⊔ ℓ₂)) where
field
: {B : Set c}
(f : B Carrier)
IsDirectedFamily _≈_ _≤_ f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition you give of IsDirectedFamily is only accidentally dependent on the underlying Setoid equality relation _≈_, so either you need to involve it by making f a suitable congruence wrt it, or else reconsider whether IsDirectedFamily really belongs in Relation.Binary.Structures?

As this stands, I think the design doesn't quite fit properly with the existing stdlib hierarchy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, it's not a priori obvious that should depend on its dir argument, as opposed to that clearly being a necessary precondition to ⋁-isLub...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would no longer be a DCPO if that were the case.

Carrier
⋁-isLub : {B : Set c}
(f : B Carrier)
(dir : IsDirectedFamily _≈_ _≤_ f)
IsLub f (⋁ f dir)

module _ {B : Set c} {f : B Carrier} {dir : IsDirectedFamily _≈_ _≤_ f} where
open IsLub (⋁-isLub f dir)
renaming (isUpperBound to ⋁-≤; isLeast to ⋁-least)
public

------------------------------------------------------------------------
-- Scott‐continuous maps between two (possibly different‐universe) posets
------------------------------------------------------------------------

module _ (P : Poset c₁ ℓ₁₁ ℓ₁₂) (Q : Poset c₂ ℓ₂₁ ℓ₂₂)
where
module P = Poset P
module Q = Poset Q

record IsScottContinuous (f : P.Carrier Q.Carrier) (κ : Level) : Set (suc (κ ⊔ c₁ ⊔ ℓ₁₁ ⊔ ℓ₁₂ ⊔ c₂ ⊔ ℓ₂₁ ⊔ ℓ₂₂))
where
field
preservelub : {I : Set κ} {g : I _} lub IsLub P g lub IsLub Q (f ∘ g) (f lub)
isOrderHomomorphism : IsOrderHomomorphism P._≈_ Q._≈_ P._≤_ Q._≤_ f

open IsOrderHomomorphism isOrderHomomorphism public
Comment on lines +58 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks as though it better belongs under Relation.Binary.Domain.Morphism.Structures, defining, say IsContinuousHomomorphism (Scott feels optional here? unless you think that such a qualifier is specific to directed complete orders?), and with a field name ⋁-homo would be more consistent with existing cognate names under Algebra.Morphism.Structures.IsXHomomorphism?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also: #2812 . Can the definition be simplified to exploit that:

  • preservesLub implies mono
  • mono implies cong in the presence of antisym

and hence that preservesLub suffices?

11 changes: 11 additions & 0 deletions src/Relation/Binary/Structures.agda
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,17 @@ record IsDenseLinearOrder (_<_ : Rel A ℓ₂) : Set (a ⊔ ℓ ⊔ ℓ₂) wher
open IsStrictTotalOrder isStrictTotalOrder public


------------------------------------------------------------------------
-- DirectedFamily
------------------------------------------------------------------------

record IsDirectedFamily {b : Level} {B : Set b} (_≤_ : Rel A ℓ₂) (f : B → A) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this definition belongs in Relation.Binary.Structures, because it's not actually (an extension of) any of the existing relational structures, rather it's a property that any one of them might additionally possess?

Set (a ⊔ b ⊔ ℓ₂) where
field
elt : B
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to revisit this name before committing to it? eg inhabitant as an alternative?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Else reuse the symbol from Relation.Construct.Add.Point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, why make the point explicit (in B) at all?
Is this definition equivalent to one where, instead of f : B → A with b : B, we instead consider arbitrary f : B → A, together with rephrasing SemiDirected in terms of Maybe B → A (so the assumption shifts to A being inhabited..., so that such a lifting is definable?), together with the canonical lifting of a relation on B to one on Maybe B which makes nothing ≤ anything and just x ≤ just y = x ≤ y?

Copy link
Contributor

@TOTBWF TOTBWF Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the definition much clunkier to use for types that are already inhabited. For example, every DCPO is an omega-CPO, but proving this with a Maybe in the definition of a directed family would require you to fiddle with Maybe Nat when constructing the family which adds a lot of incidental complexity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #2811 for comparison.

Copy link
Contributor

@TOTBWF TOTBWF Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two are not equivalent: it's important that the distinguished point be in the set/family we are taking a join of. Joins over the empty set give you a bottom element, and not every DCPO has a bottom.

A good counter example of this is the order on non-empty subsets of Nat (though any type with at least two distinct elements will do). This actually has joins of all nonempty subsets via union, and is nonempty. However, it does not have a bottom.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm clear about making the bottom-/non-bottom- containing distinction in #2811 ; my real concern with making b part of the data for a directed family, rather than its image under f in A is one of artificiality/unnaturalness in terms of the encoding, when, at least morally as I understand it, and the counterexamples don't appear to contradict, is that such f b can be chosen so as to be a lower bound on any hypothetical lub associated with the family. That's the view made concrete in #2811 , and indeed as you point out, is reconciled (or at least, appears to be) with the one here in the presence of bottoms. But you're right, I should try to express your example in Pow(Nat) in those terms, so thanks for that!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it currently stands, #2811 has the problem I mentioned with bottoms.

You can try and ask for an element of A along with a point in the setoidal fibre of the family (EG, an x : P and a b : B such that f b P.≈ x but this is quite a bit of extra data.

The definition here already has too much data to begin with, as we should only require the mere existence of an element of B and the mere existence of upper bounds. A 100% correct definition would impose an equivalence relation on the type of directed families that essentially truncates away the point and upper bounds, and then require that the joins be congruent with respect to that relation. However, this seems to be against the stdlib style, as these sorts of truncations don't seem common.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the last paragraph, one way to introduce truncation, at least as I imperfectly understand it, would be to mark fields as irrelevant with a dot, so that there can be no dependency in their actual witness. But that may still not be enough for a 1lab-style definition.

But such considerations are taking us well beyond this individual PR... so I think in the interests of making progress on it, I'm going to step back from the reviewing discussion.

isSemidirected : SemiDirected _≤_ f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be consistent... wrt our naming conventions camelCase : CamelCase, nor wrt use of isX to refer to an IsX *structure...

Suggested change
isSemidirected : SemiDirected _≤_ f
semiDirected : SemiDirected _≤_ f

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last thoughts on this/suggestion:

  • because the concept of DirectedFamily is used only as an argument to the definitions of and ⋁-isLub, it's probably simpler still to avoid reifying the notion entirely, and simply make a telescope of assumptions
  • as elsewhere in this discussion, if you want to remove explicit dependency on any choice of b : B, then it might be easiest to mark that argument as irrelevant

yielding:

      :  {I : Set c}  .I  (f : I  Carrier)  Directed (_≤_ on f)  Carrier
      ⋁-isLub :  {I : Set c} .(i : I) (f : I  Carrier) (d : Directed (_≤_ on f))  IsLub f (⋁ i f dir)



------------------------------------------------------------------------
-- Apartness relations
------------------------------------------------------------------------
Expand Down