Zulip Chat Archive
Stream: lean4
Topic: Lean 4 set_like
Kevin Buzzard (Jun 30 2021 at 17:57):
I'll be the first to admit I don't understand all this coe_t stuff. Here in Lean 3's mathlib there's a has_coe_t
instance for a very simple structure:
set_option old_structure_cmd true
/-- A class to indicate that there is a canonical injection between `A` and `set B`. -/
@[protect_proj]
class set_like (A : Type*) (B : out_param $ Type*) :=
(coe : A → set B)
(coe_injective' : function.injective coe)
namespace set_like
variables {A : Type*} {B : Type*} [i : set_like A B]
include i
instance : has_coe_t A (set B) := ⟨set_like.coe⟩
How should the has_coe_t
be translated into Lean 4?
Kevin Buzzard (Jun 30 2021 at 18:01):
I may as well ask about the next part too:
@[priority 100]
instance : has_mem B A := ⟨λ x p, x ∈ (p : set B)⟩
-- `dangerous_instance` does not know that `B` is used only as an `out_param`
@[nolint dangerous_instance, priority 100]
instance : has_coe_to_sort A := ⟨_, λ p, {x : B // x ∈ p}⟩
Can Lean 4 do priorities and should these be low in Lean 4? I've seen priority := low
in lean 4 but only for macros.
Kevin Buzzard (Jun 30 2021 at 18:12):
This compiles (if you've defined Set, e.g. like in mathlib4)
variable {A : Type u} {B : Type v} [i : set_like A B]
instance [i : set_like A B] : CoeT A a (Set B) := ⟨set_like.coe a⟩
but I don't know if I'm supposed to be using CoeT in Lean 4 because I understand what's going on properly yet.
Daniel Selsam (Jun 30 2021 at 19:54):
Kevin Buzzard said:
How should the
has_coe_t
be translated into Lean 4?
The direct translation seems to work:
def Set (α : Type u) := α → Prop
def Injective (f : α → β) : Prop :=
∀ x y, f x = f y → x = y
class SetLike (A : Type u) (B : outParam (Type v)) where
(coe : A → Set B)
(coeInjective : Injective coe)
instance [SetLike α β] : Coe α (Set β) := ⟨SetLike.coe⟩
Are you asking about the _t
part in particular? Or is it not finding the coercion later on when you expect it to?
Daniel Selsam (Jun 30 2021 at 20:04):
Kevin Buzzard said:
Can Lean 4 do priorities and should these be low in Lean 4? I've seen
priority := low
in lean 4 but only for macros.
Yes, this works:
class Foo where
x : Bool
instance (priority := low) : Foo := ⟨true⟩
example : Foo.x = true := rfl
instance (priority := high) : Foo := ⟨false⟩
example : Foo.x = false := rfl
instance (priority := low) : Foo := ⟨true⟩
example : Foo.x = false := rfl
Kevin Buzzard (Jun 30 2021 at 22:12):
Daniel Selsam said:
Are you asking about the
_t
part in particular? Or is it not finding the coercion later on when you expect it to?
Exactly -- I don't know what coe_t
means. I see that your Lean 4 translation of the Lean 3 code uses Coe
. Because I don't understand why coe_t
was used in Lean 3 (@Eric Wieser ?) and I also don't understand the differences between the Lean 3 and Lean 4 type class algorithm, I am unclear about how to port this file correctly.
Mac (Jun 30 2021 at 22:22):
Kevin Buzzard said:
Exactly -- I don't know what
coe_t
means. I see that your Lean 4 translation of the Lean 3 code usesCoe
. Because I don't understand whycoe_t
was used in Lean 3 (Eric Wieser ?) and I also don't understand the differences between the Lean 3 and Lean 4 type class algorithm, I am unclear about how to port this file correctly.
Lean 4 also has its coe_t
, named CoeT
. To quote the definition of CoeT
in the source, it:
"Combines CoeHead
, CoeTC
, CoeTail
, CoeDep
" where CoeHead
is for expensive initial coercions, CoeTC
is the transitive closure of Coe
, CoeTail
is for expensive final coercions, CoeDep
for dependently-typed coercions. In essence, CoeT
is the sum of all the basic term coercions. The Coe
library also includes CoeHTCT
for when you don't need CoeDep
in the mix.
Kevin Buzzard (Jun 30 2021 at 22:23):
The problem is that I am a mathematician and have no concept of what is expensive, for me maths runs at infinite speed and all these coercions are obvious.
Mac (Jun 30 2021 at 22:24):
Then just define Coe
instances use CoeT
in class constraints and let those with more CS experience who notice slowdowns redefine them as needed. ;)
Gabriel Ebner (Jul 01 2021 at 07:51):
Kevin Buzzard said:
How should the
has_coe_t
be translated into Lean 4?
A lot of people here have already commented on what the Coe* classes do in Lean 4, but nobody has mentioned so far why we're using has_coe_t
in Lean 3 in the first place. The reason is simple: termination. We want all type-class query to terminate, quickly, and no matter whether they fail or succeed.
Therefore the general rule is that coercion instances where (simplifying a bit) one side is a variable must be has_coe_t
instead of has_coe
. For example has_coe α (option α)
would be super bad, since you can chain it transitively to has_coe α (option (option α))
, etc. Type class synthesis has changed considerably in Lean 4, but I believe this source of nontermination is still present.
For the set_like
coercion, nontermination of transitive chaining is not an issue in practice. This is because all set_like
instances are all of the form set_like (subsemiring R) R
. Therefore it is ok to use Coe
for set_like
. (The mathlib linter rightfully complains, since it doesn't know about this special invariant of the declared set_like
instances).
instance [SetLike A B] : Coe A (Set B) where
coe := set_like.coe
(or even better, make SetLike extend Coe)
Gabriel Ebner (Jul 01 2021 at 07:58):
Kevin Buzzard said:
Can Lean 4 do priorities and should these be low in Lean 4? I've seen
priority := low
in lean 4 but only for macros.
The relevant question here is not "can Lean 4 do priorities" but "why are we using them in Lean 3". Almost all occurrences of instance priorities are to work around performance issues with type class synthesis, as described in this library note: https://leanprover-community.github.io/mathlib_docs/notes.html#lower%20instance%20priority
Therefore I would suggest to leave out the instance priorities in Lean 4 code for now, until we have a better idea about the performance cliffs of the new type class synthesis algorithm.
Side note: instance priorities are extremely finicky, and should be avoided except as a last resort when you absolutely know what you're doing.
Mac (Jul 01 2021 at 17:12):
Gabriel Ebner said:
Kevin Buzzard said:
How should the
has_coe_t
be translated into Lean 4?A lot of people here have already commented on what the Coe* classes do in Lean 4, but nobody has mentioned so far why we're using
has_coe_t
in Lean 3 in the first place. The reason is simple: termination. We want all type-class query to terminate, quickly, and no matter whether they fail or succeed.For example
has_coe α (option α)
would be super bad, since you can chain it transitively tohas_coe α (option (option α))
, etc. Type class synthesis has changed considerably in Lean 4, but I believe this source of nontermination is still present.
In that case, in Lean 4, you would want to always use Coe
rather than CoeT
, as CoeT
is the full super transitive closure. Right?
Gabriel Ebner (Jul 01 2021 at 17:15):
Coe
is of course still bad because it is chained transitively. You can now choose between CoeHead
, CoeTC
, CoeTail
, CoeHTCT
, and CoeT
for the option coercion, since none of these are chained. (The core Lean 4 library chooses CoeTail
.)
Mac (Jul 01 2021 at 17:17):
On a separate question:
Gabriel Ebner said:
Side note: instance priorities are extremely finicky, and should be avoided except as a last resort when you absolutely know what you're doing.
They are? Sebastian had me use them for properly ordering derivative MonadReader
/MonadState
instances (see this Zulip thread) and his statements there made it sound like they were the preferred way to do that.
Mac (Jul 01 2021 at 17:19):
Gabriel Ebner said:
Coe
is of course still bad because it is chained transitively. You can now choose betweenCoeHead
,CoeTC
,CoeTail
,CoeHTCT
, andCoeT
for the option coercion, since none of these are chained. (The core Lean 4 library choosesCoeTail
.)
I'm confused. So a Coe
constraint is bad because it is a link in the chain? I would think the transitive classes themselves (i.e., CoeHTCT
/ CoeT
) would be the problem for nontermination, not the Coe
links in the chian.
Sebastian Ullrich (Jul 01 2021 at 17:21):
The monad classes are a rare case where we explicitly want overlapping instances to solve mtl's "n * m instances* problem. We carefully select paths through the diamonds created by the overlapping via priorities (or declaration order).
Gabriel Ebner (Jul 01 2021 at 17:24):
Mac said:
I'm confused. So a
Coe
constraint is bad because it is a link in the chain? I would think the transitive classes themselves (i.e.,CoeHTCT
/CoeT
) would be the problem for nontermination, not theCoe
links in the chian.
I'm not talking about constraints. Declaring a Coe
instance can be bad, because the coercion mechanism looks for a CoeT
instance---and this can apply the Coe
instance an unbounded number of times.
Mac (Jul 01 2021 at 17:25):
Ah, I misunderstood. I thought you were talking about constraints not declarations. In that case, yeah, CoeHead
/CoeTail
are the goto classes.
Gabriel Ebner (Jul 01 2021 at 17:26):
My comment about instance priorities was directed at the "I want a different addition on Nat, so I'm going to declare instance (priority := high) : AddMonoid Nat
" idea I see sometimes.
Mac (Jul 01 2021 at 17:27):
That would still work, right? As that goes to overlapping instance idea that Sebastian mentioned. It is, however, an admittedly less than ideal usage,, though.
Gabriel Ebner (Jul 01 2021 at 17:30):
It probably doesn't work: when Lean looks for a Add Nat
instance it most likely tries instAddNat
first instead of AddMonoid.toAdd
(because you've only changed the priority of the AddMonoid instance, but not of any Add instances).
Mac (Jul 01 2021 at 23:26):
Ah, makes sense! That is a really good example of what kind of brittleness instance priorities have.
Last updated: Dec 20 2023 at 11:08 UTC