------------------------------------------------------------------------
-- The Agda standard library
--
-- Bundles for types of functions
------------------------------------------------------------------------
-- The contents of this file should usually be accessed from `Function`.
-- Note that these bundles differ from those found elsewhere in other
-- library hierarchies as they take Setoids as parameters. This is
-- because a function is of no use without knowing what its domain and
-- codomain is, as well which equalities are being considered over them.
-- One consequence of this is that they are not built from the
-- definitions found in `Function.Structures` as is usually the case in
-- other library hierarchies, as this would duplicate the equality
-- axioms.
{-#
OPTIONS
--cubical-compatible
--safe
#-}
module
Function.Bundles
where
open
import
Function.Base
using
(
_∘_
)
open
import
Function.Definitions
import
Function.Structures
as
FunctionStructures
open
import
Level
using
(
Level
;
_⊔_
;
suc
)
open
import
Data.Product.Base
using
(
_,_
;
proj₁
;
proj₂
)
open
import
Relation.Binary.Bundles
using
(
Setoid
)
open
import
Relation.Binary.Core
using
(
_Preserves_⟶_
)
open
import
Relation.Binary.PropositionalEquality.Core
as
≡
using
(
_≡_
)
import
Relation.Binary.PropositionalEquality.Properties
as
≡
open
import
Function.Consequences.Propositional
open
Setoid
using
(
isEquivalence
)
private
variable
a
b
ℓ₁
ℓ₂
:
Level
------------------------------------------------------------------------
-- Setoid bundles
------------------------------------------------------------------------
module
_
(
From
:
Setoid
a
ℓ₁
)
(
To
:
Setoid
b
ℓ₂
)
where
open
Setoid
From
using
()
renaming
(
Carrier
to
A
;
_≈_
to
_≈₁_
)
open
Setoid
To
using
()
renaming
(
Carrier
to
B
;
_≈_
to
_≈₂_
)
open
FunctionStructures
_≈₁_
_≈₂_
------------------------------------------------------------------------
-- Bundles with one element
-- Called `Func` rather than `Function` in order to avoid clashing
-- with the top-level module.
record
Func
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
cong
:
Congruent
_≈₁_
_≈₂_
to
isCongruent
:
IsCongruent
to
isCongruent
=
record
{
cong
=
cong
;
isEquivalence₁
=
isEquivalence
From
;
isEquivalence₂
=
isEquivalence
To
}
open
IsCongruent
isCongruent
public
using
(
module
Eq₁
;
module
Eq₂
)
record
Injection
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
cong
:
Congruent
_≈₁_
_≈₂_
to
injective
:
Injective
_≈₁_
_≈₂_
to
function
:
Func
function
=
record
{
to
=
to
;
cong
=
cong
}
open
Func
function
public
hiding
(
to
;
cong
)
isInjection
:
IsInjection
to
isInjection
=
record
{
isCongruent
=
isCongruent
;
injective
=
injective
}
record
Surjection
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
cong
:
Congruent
_≈₁_
_≈₂_
to
surjective
:
Surjective
_≈₁_
_≈₂_
to
function
:
Func
function
=
record
{
to
=
to
;
cong
=
cong
}
open
Func
function
public
hiding
(
to
;
cong
)
isSurjection
:
IsSurjection
to
isSurjection
=
record
{
isCongruent
=
isCongruent
;
surjective
=
surjective
}
open
IsSurjection
isSurjection
public
using
(
strictlySurjective
)
to⁻
:
B
→
A
to⁻
=
proj₁
∘
surjective
to∘to⁻
:
∀
x
→
to
(
to⁻
x
)
≈₂
x
to∘to⁻
=
proj₂
∘
strictlySurjective
record
Bijection
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
cong
:
Congruent
_≈₁_
_≈₂_
to
bijective
:
Bijective
_≈₁_
_≈₂_
to
injective
:
Injective
_≈₁_
_≈₂_
to
injective
=
proj₁
bijective
surjective
:
Surjective
_≈₁_
_≈₂_
to
surjective
=
proj₂
bijective
injection
:
Injection
injection
=
record
{
cong
=
cong
;
injective
=
injective
}
surjection
:
Surjection
surjection
=
record
{
cong
=
cong
;
surjective
=
surjective
}
open
Injection
injection
public
using
(
isInjection
)
open
Surjection
surjection
public
using
(
isSurjection
;
to⁻
;
strictlySurjective
)
isBijection
:
IsBijection
to
isBijection
=
record
{
isInjection
=
isInjection
;
surjective
=
surjective
}
open
IsBijection
isBijection
public
using
(
module
Eq₁
;
module
Eq₂
)
------------------------------------------------------------------------
-- Bundles with two elements
module
_
(
From
:
Setoid
a
ℓ₁
)
(
To
:
Setoid
b
ℓ₂
)
where
open
Setoid
From
using
()
renaming
(
Carrier
to
A
;
_≈_
to
_≈₁_
)
open
Setoid
To
using
()
renaming
(
Carrier
to
B
;
_≈_
to
_≈₂_
)
open
FunctionStructures
_≈₁_
_≈₂_
record
Equivalence
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from-cong
:
Congruent
_≈₂_
_≈₁_
from
toFunction
:
Func
From
To
toFunction
=
record
{
to
=
to
;
cong
=
to-cong
}
open
Func
toFunction
public
using
(
module
Eq₁
;
module
Eq₂
)
renaming
(
isCongruent
to
to-isCongruent
)
fromFunction
:
Func
To
From
fromFunction
=
record
{
to
=
from
;
cong
=
from-cong
}
open
Func
fromFunction
public
using
()
renaming
(
isCongruent
to
from-isCongruent
)
record
LeftInverse
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from-cong
:
Congruent
_≈₂_
_≈₁_
from
inverseˡ
:
Inverseˡ
_≈₁_
_≈₂_
to
from
isCongruent
:
IsCongruent
to
isCongruent
=
record
{
cong
=
to-cong
;
isEquivalence₁
=
isEquivalence
From
;
isEquivalence₂
=
isEquivalence
To
}
isLeftInverse
:
IsLeftInverse
to
from
isLeftInverse
=
record
{
isCongruent
=
isCongruent
;
from-cong
=
from-cong
;
inverseˡ
=
inverseˡ
}
open
IsLeftInverse
isLeftInverse
public
using
(
module
Eq₁
;
module
Eq₂
;
strictlyInverseˡ
;
isSurjection
)
equivalence
:
Equivalence
equivalence
=
record
{
to-cong
=
to-cong
;
from-cong
=
from-cong
}
isSplitSurjection
:
IsSplitSurjection
to
isSplitSurjection
=
record
{
from
=
from
;
isLeftInverse
=
isLeftInverse
}
surjection
:
Surjection
From
To
surjection
=
record
{
to
=
to
;
cong
=
to-cong
;
surjective
=
λ
y
→
from
y
,
inverseˡ
}
record
RightInverse
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from-cong
:
from
Preserves
_≈₂_
⟶
_≈₁_
inverseʳ
:
Inverseʳ
_≈₁_
_≈₂_
to
from
isCongruent
:
IsCongruent
to
isCongruent
=
record
{
cong
=
to-cong
;
isEquivalence₁
=
isEquivalence
From
;
isEquivalence₂
=
isEquivalence
To
}
isRightInverse
:
IsRightInverse
to
from
isRightInverse
=
record
{
isCongruent
=
isCongruent
;
from-cong
=
from-cong
;
inverseʳ
=
inverseʳ
}
open
IsRightInverse
isRightInverse
public
using
(
module
Eq₁
;
module
Eq₂
;
strictlyInverseʳ
)
equivalence
:
Equivalence
equivalence
=
record
{
to-cong
=
to-cong
;
from-cong
=
from-cong
}
record
Inverse
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from-cong
:
Congruent
_≈₂_
_≈₁_
from
inverse
:
Inverseᵇ
_≈₁_
_≈₂_
to
from
inverseˡ
:
Inverseˡ
_≈₁_
_≈₂_
to
from
inverseˡ
=
proj₁
inverse
inverseʳ
:
Inverseʳ
_≈₁_
_≈₂_
to
from
inverseʳ
=
proj₂
inverse
leftInverse
:
LeftInverse
leftInverse
=
record
{
to-cong
=
to-cong
;
from-cong
=
from-cong
;
inverseˡ
=
inverseˡ
}
rightInverse
:
RightInverse
rightInverse
=
record
{
to-cong
=
to-cong
;
from-cong
=
from-cong
;
inverseʳ
=
inverseʳ
}
open
LeftInverse
leftInverse
public
using
(
isLeftInverse
;
strictlyInverseˡ
)
open
RightInverse
rightInverse
public
using
(
isRightInverse
;
strictlyInverseʳ
)
isInverse
:
IsInverse
to
from
isInverse
=
record
{
isLeftInverse
=
isLeftInverse
;
inverseʳ
=
inverseʳ
}
open
IsInverse
isInverse
public
using
(
module
Eq₁
;
module
Eq₂
)
------------------------------------------------------------------------
-- Bundles with three elements
record
BiEquivalence
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from₁
:
B
→
A
from₂
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from₁-cong
:
Congruent
_≈₂_
_≈₁_
from₁
from₂-cong
:
Congruent
_≈₂_
_≈₁_
from₂
record
BiInverse
:
Set
(
a
⊔
b
⊔
ℓ₁
⊔
ℓ₂
)
where
field
to
:
A
→
B
from₁
:
B
→
A
from₂
:
B
→
A
to-cong
:
Congruent
_≈₁_
_≈₂_
to
from₁-cong
:
Congruent
_≈₂_
_≈₁_
from₁
from₂-cong
:
Congruent
_≈₂_
_≈₁_
from₂
inverseˡ
:
Inverseˡ
_≈₁_
_≈₂_
to
from₁
inverseʳ
:
Inverseʳ
_≈₁_
_≈₂_
to
from₂
to-isCongruent
:
IsCongruent
to
to-isCongruent
=
record
{
cong
=
to-cong
;
isEquivalence₁
=
isEquivalence
From
;
isEquivalence₂
=
isEquivalence
To
}
isBiInverse
:
IsBiInverse
to
from₁
from₂
isBiInverse
=
record
{
to-isCongruent
=
to-isCongruent
;
from₁-cong
=
from₁-cong
;
from₂-cong
=
from₂-cong
;
inverseˡ
=
inverseˡ
;
inverseʳ
=
inverseʳ
}
biEquivalence
:
BiEquivalence
biEquivalence
=
record
{
to-cong
=
to-cong
;
from₁-cong
=
from₁-cong
;
from₂-cong
=
from₂-cong
}
------------------------------------------------------------------------
-- Other
-- A left inverse is also known as a “split surjection”.
--
-- As the name implies, a split surjection is a special kind of
-- surjection where the witness generated in the domain in the
-- function for elements `x₁` and `x₂` are equal if `x₁ ≈ x₂` .
--
-- The difference is the `from-cong` law --- generally, the section
-- (called `Surjection.to⁻` or `SplitSurjection.from`) of a surjection
-- need not respect equality, whereas it must in a split surjection.
--
-- The two notions coincide when the equivalence relation on `B` is
-- propositional equality (because all functions respect propositional
-- equality).
--
-- For further background on (split) surjections, one may consult any
-- general mathematical references which work without the principle
-- of choice. For example:
--
-- https://ncatlab.org/nlab/show/split+epimorphism.
--
-- The connection to set-theoretic notions with the same names is
-- justified by the setoid type theory/homotopy type theory
-- observation/definition that (∃x : A. P) = ∥ Σx : A. P ∥ --- i.e.,
-- we can read set-theoretic ∃ as squashed/propositionally truncated Σ.
--
-- We see working with setoids as working in the MLTT model of a setoid
-- type theory, in which ∥ X ∥ is interpreted as the setoid with carrier
-- set X and the equivalence relation that relates all elements.
-- All maps into ∥ X ∥ respect equality, so in the idiomatic definitions
-- here, we drop the corresponding trivial `cong` field completely.
SplitSurjection
:
Set
_
SplitSurjection
=
LeftInverse
module
SplitSurjection
(
splitSurjection
:
SplitSurjection
)
=
LeftInverse
splitSurjection
------------------------------------------------------------------------
-- Infix abbreviations for oft-used items
------------------------------------------------------------------------
-- Same naming convention as used for propositional equality below, with
-- appended ₛ (for 'S'etoid).
infixr
0
_⟶ₛ_
_⟶ₛ_
:
Setoid
a
ℓ₁
→
Setoid
b
ℓ₂
→
Set
_
_⟶ₛ_
=
Func
------------------------------------------------------------------------
-- Bundles specialised for propositional equality
------------------------------------------------------------------------
infix
3
_⟶_
_↣_
_↠_
_⤖_
_⇔_
_↩_
_↪_
_↩↪_
_↔_
_⟶_
:
Set
a
→
Set
b
→
Set
_
A
⟶
B
=
Func
(
≡.setoid
A
)
(
≡.setoid
B
)
_↣_
:
Set
a
→
Set
b
→
Set
_
A
↣
B
=
Injection
(
≡.setoid
A
)
(
≡.setoid
B
)
_↠_
:
Set
a
→
Set
b
→
Set
_
A
↠
B
=
Surjection
(
≡.setoid
A
)
(
≡.setoid
B
)
_⤖_
:
Set
a
→
Set
b
→
Set
_
A
⤖
B
=
Bijection
(
≡.setoid
A
)
(
≡.setoid
B
)
_⇔_
:
Set
a
→
Set
b
→
Set
_
A
⇔
B
=
Equivalence
(
≡.setoid
A
)
(
≡.setoid
B
)
_↩_
:
Set
a
→
Set
b
→
Set
_
A
↩
B
=
LeftInverse
(
≡.setoid
A
)
(
≡.setoid
B
)
_↪_
:
Set
a
→
Set
b
→
Set
_
A
↪
B
=
RightInverse
(
≡.setoid
A
)
(
≡.setoid
B
)
_↩↪_
:
Set
a
→
Set
b
→
Set
_
A
↩↪
B
=
BiInverse
(
≡.setoid
A
)
(
≡.setoid
B
)
_↔_
:
Set
a
→
Set
b
→
Set
_
A
↔
B
=
Inverse
(
≡.setoid
A
)
(
≡.setoid
B
)
-- We now define some constructors for the above that
-- automatically provide the required congruency proofs.
module
_
{
A
:
Set
a
}
{
B
:
Set
b
}
where
mk⟶
:
(
A
→
B
)
→
A
⟶
B
mk⟶
to
=
record
{
to
=
to
;
cong
=
≡.cong
to
}
mk↣
:
∀
{
to
:
A
→
B
}
→
Injective
_≡_
_≡_
to
→
A
↣
B
mk↣
{
to
}
inj
=
record
{
to
=
to
;
cong
=
≡.cong
to
;
injective
=
inj
}
mk↠
:
∀
{
to
:
A
→
B
}
→
Surjective
_≡_
_≡_
to
→
A
↠
B
mk↠
{
to
}
surj
=
record
{
to
=
to
;
cong
=
≡.cong
to
;
surjective
=
surj
}
mk⤖
:
∀
{
to
:
A
→
B
}
→
Bijective
_≡_
_≡_
to
→
A
⤖
B
mk⤖
{
to
}
bij
=
record
{
to
=
to
;
cong
=
≡.cong
to
;
bijective
=
bij
}
mk⇔
:
∀
(
to
:
A
→
B
)
(
from
:
B
→
A
)
→
A
⇔
B
mk⇔
to
from
=
record
{
to
=
to
;
from
=
from
;
to-cong
=
≡.cong
to
;
from-cong
=
≡.cong
from
}
mk↩
:
∀
{
to
:
A
→
B
}
{
from
:
B
→
A
}
→
Inverseˡ
_≡_
_≡_
to
from
→
A
↩
B
mk↩
{
to
}
{
from
}
invˡ
=
record
{
to
=
to
;
from
=
from
;
to-cong
=
≡.cong
to
;
from-cong
=
≡.cong
from
;
inverseˡ
=
invˡ
}
mk↪
:
∀
{
to
:
A
→
B
}
{
from
:
B
→
A
}
→
Inverseʳ
_≡_
_≡_
to
from
→
A
↪
B
mk↪
{
to
}
{
from
}
invʳ
=
record
{
to
=
to
;
from
=
from
;
to-cong
=
≡.cong
to
;
from-cong
=
≡.cong
from
;
inverseʳ
=
invʳ
}
mk↩↪
:
∀
{
to
:
A
→
B
}
{
from₁
:
B
→
A
}
{
from₂
:
B
→
A
}
→
Inverseˡ
_≡_
_≡_
to
from₁
→
Inverseʳ
_≡_
_≡_
to
from₂
→
A
↩↪
B
mk↩↪
{
to
}
{
from₁
}
{
from₂
}
invˡ
invʳ
=
record
{
to
=
to
;
from₁
=
from₁
;
from₂
=
from₂
;
to-cong
=
≡.cong
to
;
from₁-cong
=
≡.cong
from₁
;
from₂-cong
=
≡.cong
from₂
;
inverseˡ
=
invˡ
;
inverseʳ
=
invʳ
}
mk↔
:
∀
{
to
:
A
→
B
}
{
from
:
B
→
A
}
→
Inverseᵇ
_≡_
_≡_
to
from
→
A
↔
B
mk↔
{
to
}
{
from
}
inv
=
record
{
to
=
to
;
from
=
from
;
to-cong
=
≡.cong
to
;
from-cong
=
≡.cong
from
;
inverse
=
inv
}
-- Strict variant of the above.
mk↠ₛ
:
∀
{
to
:
A
→
B
}
→
StrictlySurjective
_≡_
to
→
A
↠
B
mk↠ₛ
=
mk↠
∘
strictlySurjective⇒surjective
mk↔ₛ′
:
∀
(
to
:
A
→
B
)
(
from
:
B
→
A
)
→
StrictlyInverseˡ
_≡_
to
from
→
StrictlyInverseʳ
_≡_
to
from
→
A
↔
B
mk↔ₛ′
to
from
invˡ
invʳ
=
mk↔
{
to
}
{
from
}
(
strictlyInverseˡ⇒inverseˡ
to
invˡ
,
strictlyInverseʳ⇒inverseʳ
to
invʳ
)
------------------------------------------------------------------------
-- Other
------------------------------------------------------------------------
-- Alternative syntax for the application of functions
module
_
{
From
:
Setoid
a
ℓ₁
}
{
To
:
Setoid
b
ℓ₂
}
where
open
Setoid
infixl
5
_⟨$⟩_
_⟨$⟩_
:
Func
From
To
→
Carrier
From
→
Carrier
To
_⟨$⟩_
=
Func.to