Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setoid instances #10

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Setoid instances #10

wants to merge 8 commits into from

Conversation

jkopanski
Copy link
Contributor

Following discussion we had last week I've started to define those
instances and it occurred to me that they would make nice addition to
the base library.

I've removed requirement for Products (Set q) instance from lawful
CartesianClosed. It seems it wasn't used anywhere and I didn't know
what that would be here.

Provided instances quantify Levels individually as they're different
for different typeclasses. Quantifiyng that at the module level would
mean that we wouldn't be able to substitute it differently. For
example: Products can use Setoid c ℓ as obj. But
Exponentials require that obj is Setoid (c ⊔ ℓ) (c ⊔ ℓ), which
in turns requires Products for the same obj. Fixing c and
at the module would prohibit Exponentials to get required
Products.

One module (Function.Construct.Setoid) was copied from
agda-categories, and there it was copied from old standard library, as
it got removed from the 2.0 version.

Last commit is my attempt at defining Traced, but I can't quite get it
work. See the commit comment.

@conal
Copy link
Owner

conal commented Feb 6, 2024

Thanks, Jakub! I think we will need this category.

@conal
Copy link
Owner

conal commented Feb 6, 2024

I've removed requirement for Products (Set q) instance from lawful
CartesianClosed. It seems it wasn't used anywhere and I didn't know
what that would be here.

Great. Perhaps this constraint was needed at one time and then no longer.

@conal
Copy link
Owner

conal commented Feb 6, 2024

Provided instances quantify Levels individually as they're different
for different typeclasses. Quantifiyng that at the module level would
mean that we wouldn't be able to substitute it differently. For
example: Products can use Setoid c ℓ as obj. But
Exponentials require that obj is Setoid (c ⊔ ℓ) (c ⊔ ℓ), which
in turns requires Products for the same obj. Fixing c and
at the module would prohibit Exponentials to get required
Products.

:D !

@conal
Copy link
Owner

conal commented Feb 6, 2024

One module (Function.Construct.Setoid) was copied from agda-categories, and there it was copied from old standard library, as it got removed from the 2.0 version.

Where was this functionality in the pre-2.0 agda-stdlib?

@jkopanski
Copy link
Contributor Author

One module (Function.Construct.Setoid) was copied from agda-categories, and there it was copied from old standard library, as it got removed from the 2.0 version.

Where was this functionality in the pre-2.0 agda-stdlib?

I think it was Function.Equality..

@jkopanski
Copy link
Contributor Author

Provided instances quantify Levels individually as they're different
for different typeclasses. Quantifiyng that at the module level would
mean that we wouldn't be able to substitute it differently. For
example: Products can use Setoid c ℓ as obj. But
Exponentials require that obj is Setoid (c ⊔ ℓ) (c ⊔ ℓ), which
in turns requires Products for the same obj. Fixing c and
at the module would prohibit Exponentials to get required
Products.

:D !

does that mean my explanation is incomprehensible? I don't know what you mean by that smiley

@conal
Copy link
Owner

conal commented Feb 7, 2024

One module (Function.Construct.Setoid) was copied from agda-categories, and there it was copied from old standard library, as it got removed from the 2.0 version.

Where was this functionality in the pre-2.0 agda-stdlib?

I think it was Function.Equality..

Thank you! That's the definition I was trying to remember. Do you have any idea why it was removed?

@jkopanski
Copy link
Contributor Author

One module (Function.Construct.Setoid) was copied from agda-categories, and there it was copied from old standard library, as it got removed from the 2.0 version.

Where was this functionality in the pre-2.0 agda-stdlib?

I think it was Function.Equality..

Thank you! That's the definition I was trying to remember. Do you have any idea why it was removed?

I was trying to look up the relevant agda-categories commit history in hopes to figure out more. I know nothing for sure but my guess it was just forgotten.

Note that Jacques already opened this as an stdlib PR with v2.1 tag.

@conal
Copy link
Owner

conal commented Feb 7, 2024

:D !

does that mean my explanation is incomprehensible? I don't know what you mean by that smiley

I meant I liked what you did.

@conal
Copy link
Owner

conal commented Feb 7, 2024

There are a lot of opportunities to use standard categorical operations here. Did you consider doing so?

@jkopanski
Copy link
Contributor Author

There are a lot of opportunities to use standard categorical operations here. Did you consider doing so?

Not really, it was mostly mechanistic translation from Function instances. I do however think that might be the case to cracking Traced as it would perhaps allow me to not reference directly the value that comes from well foundedness and instead do the same operation on the both sides of equality.

What did occur to me after this is that Func.to is homomorphism into Function, and that could be used to transfer properties

@jkopanski jkopanski force-pushed the setoid-instances branch 3 times, most recently from 4d5381f to 6f2619f Compare February 9, 2024 12:00
@jkopanski
Copy link
Contributor Author

There are a lot of opportunities to use standard categorical operations here. Did you consider doing so?

I've tried this exercise and it blew my mind ;) see, comments attached to a related commit: 78e655a

@conal
Copy link
Owner

conal commented Feb 9, 2024

There are a lot of opportunities to use standard categorical operations here. Did you consider doing so?

I’ve tried this exercise and it blew my mind ;) see, comments attached to a related commit: 78e655a

Terrific! It sounds like you’re having the experience I hoped you would. Start with poetic rephrasings, see what new hints appear, and follow where the beauty seems to lead.

@conal
Copy link
Owner

conal commented Feb 9, 2024

What did occur to me after this is that Func.to is homomorphism into Function, and that could be used to transfer properties

For properties to transfer, equivalence on the homomorphism’s source must be defined as equivalence of homomorphic images. When the homomorphism maps representations to their meanings, this definition of equivalence is necessary (I think) for non-leaky abstraction. What notion or notions of morphism equivalence make sense in a Setoid category?

@conal
Copy link
Owner

conal commented Feb 9, 2024

From commit 78e655a:

This has completely stunned me. Law for the categorical operator holds by the definition of this operator in different category! I find it amazing!

I’m glad that you found this surprise yourself and that you enjoy the gift! I’ve stumbled onto such homomorphic gems often enough that I now know to dig for them. Finding them in logic/proofs is even more satisfying than in implementations, further rewarding the switch from Haskell to Agda.

@jkopanski
Copy link
Contributor Author

What did occur to me after this is that Func.to is homomorphism into Function, and that could be used to transfer properties

For properties to transfer, equivalence on the homomorphism’s source must be defined as equivalence of homomorphic images. When the homomorphism maps representations to their meanings, this definition of equivalence is necessary (I think) for non-leaky abstraction. What notion or notions of morphism equivalence make sense in a Setoid category?

IIUC that is exactly the case for equivalence in category of setoids: ∀ x → to f x ≈ to g x. Ok now that I've pasted this in, it is not exactly the case, as in both categories we pointwise equality and it's not exact. So it should be rewritten in terms of _≈_ from Equivalent for function instance?

@conal
Copy link
Owner

conal commented Feb 9, 2024

IIUC that is exactly the case for equivalence in category of setoids: ∀ x → to f x ≈ to g x. Ok now that I've pasted this in, it is not exactly the case, as in both categories we pointwise equality and it's not exact. So it should be rewritten in terms of _≈_ from Equivalent for function instance?

If we want equational properties (laws etc) to transfer backward through to in the standard homomorphic way, I think we’d have to define _≈_ = _≈_ on to, i.e., f ≈ g = to f ≈ to g, i.e., f ≈ g = ∀ x → to f x ≡ to g x. (You might enjoy reading through Relation.Binary.Construct.On if you haven’t already.) This _≈_ definition doesn’t, however, match the intent of Setoid.

An alternative is to use a homomorphism going the other way: specify the function category (“Set”) via a homomorphism to the setoid category rather than vice versa.

@jkopanski jkopanski force-pushed the setoid-instances branch 2 times, most recently from b3a92c6 to 79b4672 Compare February 19, 2024 21:35
src/Felix/Instances/Setoid/Type.agda Outdated Show resolved Hide resolved
src/Felix/Laws.agda Show resolved Hide resolved
src/Felix/Instances/Setoid/Raw.agda Outdated Show resolved Hide resolved
src/Felix/Instances/Setoid/Type.agda Outdated Show resolved Hide resolved
Forgot to add this one previously.  It's not included in the
`Felix.Instances.Function` as the rest of the Function directory.
@jkopanski jkopanski force-pushed the setoid-instances branch 2 times, most recently from 381c3fb to 445490e Compare February 22, 2024 13:29
Comment on lines 65 to 81
distributive = record
{ distribˡ⁻¹ = record
{ to = λ (a , bc) → [ inj₁ ∘ᵈ (a ,_) , inj₂ ∘ᵈ (a ,_) ] bc
; cong = λ where
(a₁≈a₂ , inj₁ b₁≈b₂) → inj₁ (a₁≈a₂ , b₁≈b₂)
(a₁≈a₂ , inj₂ c₁≈c₂) → inj₂ (a₁≈a₂ , c₁≈c₂)
}
; distribʳ⁻¹ = record
{ to = λ (bc , a) → [ inj₁ ∘ᵈ (_, a) , inj₂ ∘ᵈ (_, a) ] bc
; cong = λ where
(inj₁ b₁≈b₂ , a₁≈a₂) → inj₁ (b₁≈b₂ , a₁≈a₂)
(inj₂ c₁≈c₂ , a₁≈a₂) → inj₂ (c₁≈c₂ , a₁≈a₂)
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

How tidy could these definitions become if rewritten via the categorical vocabulary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh, I took me way too long to notice, still can't replace cong one as I can't guide Agda to pick proper instances, which I myself don't know in full details. I was hoping agda would figure it out. All I see is the pattern for distribl/r

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, the issue here is that constructors (inj₁/inj₂) used at cong are coming from Pointwise and we don't have any category instance that would use this as coproducts*. I've tried using map from that module but I have trouble convincing Agda is complaining about stuff that I don't know how to solve.

Copy link
Owner

Choose a reason for hiding this comment

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

Ok, the issue here is that constructors (inj₁/inj₂) used at cong are coming from Pointwise [...]

I don't know, and I like this question a lot! If we come up with a good answer, this whole Setoid category might come out beautifully.

Copy link
Owner

Choose a reason for hiding this comment

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

Note that categorical products and coproducts in Rels are necessarily (isomorphic to) sum types.

Comment on lines +52 to +65
distributive = record
{ distribˡ∘distribˡ⁻¹ = λ where
{A} {B} {C} (_ , inj₁ _) → Setoid.refl (A ×ₛ (B ⊎ₛ C))
{A} {B} {C} (_ , inj₂ _) → Setoid.refl (A ×ₛ (B ⊎ₛ C))
; distribˡ⁻¹∘distribˡ = λ where
{A} {B} {C} (inj₁ _) → Setoid.refl ((A ×ₛ B) ⊎ₛ (A ×ₛ C))
{A} {B} {C} (inj₂ _) → Setoid.refl ((A ×ₛ B) ⊎ₛ (A ×ₛ C))
; distribʳ∘distribʳ⁻¹ = λ where
{A} {B} {C} (inj₁ _ , _) → Setoid.refl ((B ⊎ₛ C) ×ₛ A)
{A} {B} {C} (inj₂ _ , _) → Setoid.refl ((B ⊎ₛ C) ×ₛ A)
; distribʳ⁻¹∘distribʳ = λ where
{A} {B} {C} (inj₁ _) → Setoid.refl ((B ×ₛ A) ⊎ₛ (C ×ₛ A))
{A} {B} {C} (inj₂ _) → Setoid.refl ((B ×ₛ A) ⊎ₛ (C ×ₛ A))
}
Copy link
Owner

@conal conal Feb 22, 2024

Choose a reason for hiding this comment

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

How pretty could these proofs look if expressed via categorical vocabulary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

basically variation of the (Setoid.refl ▿ Setoid.refl) ∘ exr but the level inference for proper categorical instances are killing me

coproducts = record
{ ⊥ = record
{ Carrier = ⊥′
; _≈_ = λ _ _ → ⊤′
Copy link
Owner

Choose a reason for hiding this comment

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

This one feels a little off to me. How about you?

Following discussion we had last week I've started to define those
instances and it occurred to me that they would make nice addition to
the base library.

I've removed requirement for `Products (Set q)` instance from lawful
`CartesianClosed`.  It seems it wasn't used anywhere and I didn't know
what that would be here.

Provided instances quantify `Level`s individually as they're different
for different typeclasses.  Quantifiyng that at the module level would
mean that we wouldn't be able to substitute it differently.  For
example: `Products` can use `Setoid c ℓ` as `obj`.  But
`Exponentials` require that `obj` is `Setoid (c ⊔ ℓ) (c ⊔ ℓ)`, which
in turns requires `Products` for the same `obj`.  Fixing `c` and `ℓ`
at the module would prohibit `Exponentials` to get required
`Products`.

One module (`Function.Construct.Setoid`) was copied from
agda-categories, and there it was copied from old standard library, as
it got removed from the 2.0 version.
Actually I don't use that much _categorical operators_, as I couldn't
get proper categorical instances to work.  That is, Setoid categorical
laws are functions in the category of (Agda) function and types.
However I couldn't figure out the Level of Set that I should set to
`Felix.Instance.Function` in order to use those operators in laws
definitions.  So I settled for next best thing and used standard
library provided functions.  And oh boy, did I got surprised by the
results. Taking for example the `▵≈` law from the `Cartesian`. It
holds by `<_,_>` (which is the definition of _▵_ in category of
functions and types)!

This has completely stunned me. Law for the categorical operator holds
by the definition of this operator in different category!  I find it
amazing!

The same holds for `curry` in the `CartesianClosed`,  *but* I couldn't
figure it out for `∘≈` in the `Category`.  I wonder why?

I wonder if this has something to do with the fact that there is
trivial homomorphism from the category of `Setoid`s to category of
function and types via `⟦_⟧ = Func.to`?

In order to make `CartesianClosed` simplification to work I needed to
make argument to `Setoid` of setoid functions explicit.
Unfortunately in the upcoming PR it is implicit.
By using categorical operations I don't have to come up with `cong`
proof manually.  It will be composed instead.
I don't know however if this definition is good one, so it'll be
commented out for the time being.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants