Zulip Chat Archive

Stream: mathlib4

Topic: Typeclass automatic synthesising for statements a la op...


Wrenna Robson (Jan 16 2024 at 18:10):

A question that arose while I was working on #9145: so if you have i j : Fin (n + 1), with i ≠ j, then it follows that n ≠ 0, and so in particular you have an instance of NeZero n. This is relevant because, essentially, given i j : Fin (n + 1), with i ≠ j, 0 : Fin n always makes sense. This does come up somewhere: in particular, sometimes in #9145 I have i j : Fin (n + 1), with i ≠ j, and I want to use 0 : Fin n in the statement, and I couldn't.

What I'm wondering is how to efficiently and elegantly use this fact: I don't think I can use optParam in the same way that I might if I wanted a proof of n ≠ 0 in the statement, because what I want is a typeclass synthesised from a hypothesis. Is that even possible?

Alternatively, in some ways this reduces to the two following facts:

  • Given i j : Fin (n + 1), with i ≠ j, I can always produce some p: Fin n.
  • If I have p : Fin n, the notation 0 : Fin n always makes sense (it may or may not be p).

So can I do either of those more elegantly? Do "typeclasses automatically synthesised from hypotheses" even make sense?

Notification Bot (Jan 16 2024 at 18:10):

Wrenna Robson has marked this topic as resolved.

Notification Bot (Jan 16 2024 at 18:10):

Wrenna Robson has marked this topic as unresolved.

Kyle Miller (Jan 16 2024 at 18:24):

Typeclass synthesis doesn't look at hypotheses, but something you do is make a function neZeroOfFinNe : {i j : Fin (n + 1)} -> i ≠ j -> NeZero n and then given h : i ≠ j write have := neZeroOfFinNe h either in your proof or as a line of a definition.

Wrenna Robson (Jan 16 2024 at 19:43):

Hmm, how do you fit it into a definition? Like that can be part of a statement?

Kyle Miller (Jan 16 2024 at 19:47):

You can use haveI := neZeroOfFinNe h; ... in a statement and have it be eliminated from the type. You can't use it for variables before the colon though.

Wrenna Robson (Jan 16 2024 at 21:01):

Thanks, that is perfect.

Wrenna Robson (Jan 16 2024 at 21:01):

Any suggestions where such a neZeroOfFinNe would go? Data/Fin/Basic?

Emilie (Shad Amethyst) (Jan 16 2024 at 22:14):

I would also have something of the likes of instance neZero_of_nonTrivial_fin_succ (nt : Nontrivial (Fin n.succ)) : NeZero n

Emilie (Shad Amethyst) (Jan 16 2024 at 22:21):

You also have that NeZero n ↔ NonEmpty (Fin n) ↔ Nontrivial (Fin n.succ), though I don't know if the typeclass solver can handle loops. The left equivalence is covered by docs#Fin.pos_iff_nonempty already and the right equivalence can be derived from docs#Fin.nontrivial_iff_two_le

Wrenna Robson (Jan 17 2024 at 01:15):

If we had instance neZero_of_nonTrivial_fin_succ [Nontrivial (Fin n.succ)] : NeZero n, would that then let the solver do the inference (taking it as a typeclass)?

I suspect what you want is instance [Nontrivial (Fin n.succ)] : NonEmpty (Fin n) and instance [NonEmpty (Fin n)] : NeZero n, and that way avoid the loop in the typeclass solver.

Eric Wieser (Jan 17 2024 at 02:15):

That second instance already exists in reverse

Wrenna Robson (Jan 17 2024 at 02:16):

Oops. I knew we had docs#Fin.pos_iff_nonempty but didn't realise we had the instance.

Wrenna Robson (Jan 17 2024 at 02:16):

That feels like... the less useful order in some ways? Idk.

Eric Wieser (Jan 17 2024 at 02:18):

Well, it follows from the fact that when n is not zero, there is a Zero

Wrenna Robson (Jan 17 2024 at 02:19):

Indeed.

Wrenna Robson (Jan 17 2024 at 02:23):

It's just one of those slightly troublesome things where frankly it's basically trivial except somehow it isn't.

Wrenna Robson (Jan 17 2024 at 02:26):

Annoyingly it's slightly asymmetrical as well, in the sense that you don't have a similar issue for last (precisely because last n is in Fin (n +1), I.e. there's no analogy of "NeZero means you have a last".

Eric Wieser (Jan 17 2024 at 02:29):

I think generalizing last in that way spoiled be reasonable?

Eric Wieser (Jan 17 2024 at 02:30):

Oh, I guess it maybe ends up with a bad defeq

Wrenna Robson (Jan 17 2024 at 02:31):

Yeah the issue is, what would you define last 0 as? In a sense the better notation is arguably \top

Wrenna Robson (Jan 17 2024 at 02:31):

Though obviously in general using \bot instead of 0 would be... eccentric.

Wrenna Robson (Jan 17 2024 at 02:38):

Of course you could define last : Fin n as rev 0.

Eric Wieser (Jan 17 2024 at 03:01):

Wrenna Robson said:

Yeah the issue is, what would you define last 0 as?

False.elim _

Wrenna Robson (Jan 17 2024 at 08:02):

Hmm I'm not sure that works.

Emilie (Shad Amethyst) (Jan 17 2024 at 10:14):

It's the equivalent of unreachable!() in rust

Wrenna Robson (Jan 17 2024 at 10:15):

right...

Mario Carneiro (Jan 17 2024 at 10:15):

the equivalent of unreachable!() in rust is unreachable!, the equivalent of False.elim in rust is match x {}

Wrenna Robson (Jan 17 2024 at 10:15):

I just don't quite follow what you're eliminating.

Mario Carneiro (Jan 17 2024 at 10:16):

Fin0.elim is like False.elim but it takes an element of Fin 0 instead

Wrenna Robson (Jan 17 2024 at 10:16):

Right.

Mario Carneiro (Jan 17 2024 at 10:16):

you can also just pattern match on it, the compiler knows it is empty

Wrenna Robson (Jan 17 2024 at 10:17):

But I asked what you'd define last 0 as if you made last n a member of Fin n.

Mario Carneiro (Jan 17 2024 at 10:18):

oh, I was thinking this was a function that takes a Fin n, of course if it's just producing a Fin n from nothing then you are in trouble for n = 0

Wrenna Robson (Jan 17 2024 at 10:19):

Indeed. But you can make it work if you add a [NeZero n] typeclass.

Wrenna Robson (Jan 17 2024 at 10:19):

I was just remarking that that isn't currently an option.

Emilie (Shad Amethyst) (Jan 17 2024 at 10:21):

An instance of instance [NeZero n] : OrderTop (Fin n) would be nice to have, I think.
Also, speaking of option, you could wrap the result in an Option

Wrenna Robson (Jan 17 2024 at 10:23):

Yeah I don't know if the OrderTop instance currently works for that, I think we just have the one for n +1. It can be a bit of a pain writing lemmas (in the zero case) because I never know if I should have two versions or just the [NeZero] one.

Yaël Dillies (Jan 17 2024 at 10:28):

We agreed a long time ago that instances about Fin (n + 1) should instead be instances about Fin n where NeZero n. Feel free to fix whatever instances are not following that rule.

Wrenna Robson (Jan 17 2024 at 10:28):

Oh really?

Wrenna Robson (Jan 17 2024 at 10:29):

Then I think the best strategy would be to change last n so that instead it's just called last, make it a member of Fin n, and define it as Fin.rev 0 as a bonus.

Yaël Dillies (Jan 17 2024 at 10:30):

Why not just use then?

Wrenna Robson (Jan 17 2024 at 10:30):

I was just typing that!

Wrenna Robson (Jan 17 2024 at 10:31):

Well, I don't know why we decided to have last in the first place.

Yaël Dillies (Jan 17 2024 at 10:31):

I think it's because it's easier to type (last n) than (⊤ : Fin (n + 1)) when the expected type is unavailable.

Wrenna Robson (Jan 17 2024 at 10:32):

As I was saying yesterday, arguably one could also use \bot instead of 0 in a lot of places but that is a little eccentric.

Yaël Dillies (Jan 17 2024 at 10:32):

Yeah but that's a different situation: 0 has an algebraic meaning that last n doesn't have.

Wrenna Robson (Jan 17 2024 at 10:32):

Yaël Dillies said:

I think it's because it's easier to type (last n) than (⊤ : Fin (n + 1)) when the expected type is unavailable.

I suppose you could define last n as the same, make the n explicit.

Wrenna Robson (Jan 17 2024 at 10:33):

Yaël Dillies said:

Yeah but that's a different situation: 0 has an algebraic meaning that last n doesn't have.

Yes I agree - but we tend to use 0 even in the order-theoretic places for Fin.

Wrenna Robson (Jan 17 2024 at 10:33):

Which I'm not necessarily disagreeing with, to be clear.

Wrenna Robson (Jan 17 2024 at 10:34):

But e.g. Fin.cases and Fin.lastCases are somewhat more about ordering than they are about algebraic properties.

Yaël Dillies (Jan 17 2024 at 10:34):

I'm just arguing that 0 and last n can't be thought about symmetrically because they have differing algebraic relevance (even though they have identical order theoretic relevance).

Wrenna Robson (Jan 17 2024 at 10:35):

Ah I see what you mean

Wrenna Robson (Jan 17 2024 at 10:36):

Yes, indeed. I suppose you could talk about the algebraic properties of last n (for one thing, it's the additive inverse of 1...) but I can't ever see why that would be easier.

Yaël Dillies (Jan 17 2024 at 10:36):

Well that wouldn't be last n, right? That would be -1. Spelling matters.

Wrenna Robson (Jan 17 2024 at 10:37):

True, true.

Wrenna Robson (Jan 17 2024 at 10:37):

As you say, 0 is a bit special.

Mario Carneiro (Jan 17 2024 at 10:38):

-1 is a bit pathological though, because it does something weird on Fin 1

Wrenna Robson (Jan 17 2024 at 10:38):

Does it?

Wrenna Robson (Jan 17 2024 at 10:38):

I mean it's also 0, obviously.

Yaël Dillies (Jan 17 2024 at 10:38):

How so? It's still the additive inverse of 1.

Mario Carneiro (Jan 17 2024 at 10:38):

well 1 : Fin n is 1 for every n except for n = 1, where it is 0 instead

Wrenna Robson (Jan 17 2024 at 10:39):

yes I'm not quite sure why it's not also 1.

Yaël Dillies (Jan 17 2024 at 10:39):

You mean (1 : Fin n).val.

Wrenna Robson (Jan 17 2024 at 10:39):

Oh I see what you mean.

Mario Carneiro (Jan 17 2024 at 10:39):

It works out up to (propositional) equality, but it's less uniform compared to last which is n - 1 for every n > 0

Wrenna Robson (Jan 17 2024 at 10:40):

Yes that can occasionally trip one up. Clearly the solution is to start things at Fin 2. Fin 1 and Fin 0 are silly types: let's never go there ;)

Wrenna Robson (Jan 17 2024 at 10:41):

Mario Carneiro said:

It works out up to (propositional) equality, but it's less uniform compared to last which is n - 1 for every n > 0

Yes in this sense last is closer to 0 (which makes sense!) in that it's only pathological at 0 (where nothing exists anyway).

Mario Carneiro (Jan 17 2024 at 10:41):

too bad we don't have a ~ operator

Mario Carneiro (Jan 17 2024 at 10:42):

does ~~~0 work?

Wrenna Robson (Jan 17 2024 at 10:42):

as I say this is why I think I like rev 0 as its definition.

Wrenna Robson (Jan 17 2024 at 10:42):

What would ~ do?

Mario Carneiro (Jan 17 2024 at 10:42):

rev

Wrenna Robson (Jan 17 2024 at 10:42):

Right. Yeah, would be nice.

Mario Carneiro (Jan 17 2024 at 10:42):

I mean it as in the C bitwise negation operator

Mario Carneiro (Jan 17 2024 at 10:42):

which is -n-1

Wrenna Robson (Jan 17 2024 at 10:57):

Right right. Do we use that notation for anything?

Mario Carneiro (Jan 17 2024 at 10:58):

bitwise ops

Wrenna Robson (Jan 17 2024 at 10:58):

Which... I think this would be consistent with (on Fin (2^m)?)

Wrenna Robson (Jan 17 2024 at 12:43):

@Yaël Dillies yeah it looks like the bounded order instance on Fin is for Fin (n + 1) instead of NeZero n. That wants changing, right?

Wrenna Robson (Jan 17 2024 at 12:43):

Unfortunately the definition of last is in std so it can't really change. We could have a last' that works more as we might want...

Yaël Dillies (Jan 17 2024 at 12:44):

NeZero might make it to Std eventually

Wrenna Robson (Jan 17 2024 at 12:45):

That would be nice


Last updated: May 02 2025 at 03:31 UTC