Zulip Chat Archive
Stream: general
Topic: subtyping
Bernd Losert (Dec 06 2021 at 22:42):
Say I have the following setup:
struct point : Type
struct color_point extends point: Type
f : point -> nat
p : color_point
Now I want to do f p
, but this doesn't work because the types don't match and subtyping is not a thing a lean. What do you do in this case? Do I have to make a point
from p
? What's the idiomatic way to do this?
Kevin Buzzard (Dec 06 2021 at 22:43):
You could make a coercion from color_point
to point
, or you can use p.point
.
Bernd Losert (Dec 06 2021 at 22:45):
I tried coe p
, but that didn't work. I just tried p.point
and it gives me:
invalid field notation, 'point' is not a valid "field" because environment does not contain 'color_point.point'
Eric Wieser (Dec 06 2021 at 22:46):
Can you turn your example above into code that actually compiles? Is this lean3 or lean4?
Bernd Losert (Dec 06 2021 at 22:46):
It's lean 3.
Eric Wieser (Dec 06 2021 at 22:46):
Probably you want p.to_point
Bernd Losert (Dec 06 2021 at 22:46):
Here's the code:
structure point (α : Type*) :=
mk :: (x : α) (y : α)
inductive color
| red | green | blue
structure color_point (α : Type*) extends point α :=
mk :: (c : color)
def p : color_point nat := { x := 0, y := 1, c := color.red }
def foo (p : point nat) : nat := p.x
#check foo (p.point)
Bernd Losert (Dec 06 2021 at 22:47):
Ah, to_point
works nicely. Thank you.
Eric Wieser (Dec 06 2021 at 22:47):
If you don't like .to_point
as the name, you can change it with:
structure color_point (α : Type*) extends to_not_colored_point : point α :=
Eric Wieser (Dec 06 2021 at 22:47):
(I think)
Bernd Losert (Dec 06 2021 at 22:48):
That works to. Cool.
Eric Wieser (Dec 06 2021 at 22:48):
A good trick here is #print prefix color_point
, which shows you all the generated names that structure
gave you
Eric Wieser (Dec 06 2021 at 22:48):
Which would have told you to use to_point
Bernd Losert (Dec 06 2021 at 22:48):
Aha. I didn't know about prefix
. Good to know.
Bernd Losert (Dec 06 2021 at 22:49):
Wow, that's a lot generated functions.
Bernd Losert (Dec 06 2021 at 22:51):
If you a group g
and you want to get at the semigroup, is it just g.to_semigroup
or do you have to use to_monoid
first?
Kevin Buzzard (Dec 06 2021 at 22:55):
Here's how the coercion would work:
instance (α) : has_coe (color_point α) (point α) := ⟨color_point.to_point⟩
#check foo p -- foo ↑p : ℕ
The up-arrow is notation for has_coe.coe
, which in this case is color_point.to_point
. You could also write instance (α) : has_coe (color_point α) (point α) := { coe := color_point.to_point }
-- it's the same thing.
Kevin Buzzard (Dec 06 2021 at 22:57):
Re the group question: are you rolling your own groups, are you bundling the type into the group etc etc? Can you post code? I'm not clear what g
is. In Lean we do (G : Type) [group G]
and then type class inference knows G has a semigroup structure.
Bernd Losert (Dec 06 2021 at 22:57):
Bernd Losert said:
If you a group
g
and you want to get at the semigroup, is it justg.to_semigroup
or do you have to useto_monoid
first?
Looks like g.to_semigroup
works.
Bernd Losert (Dec 06 2021 at 22:57):
At least, that's what I infer after extending the color_point
struct one-level higher.
Bernd Losert (Dec 06 2021 at 22:59):
Didn't realize there was a has_coe
class. Good to know.
Kevin Buzzard (Dec 06 2021 at 22:59):
In mathlib, groups are not done in the same way as color_point
s. They are classes, and the questions you're asking are handled by type class inference. I still don't know what g
is but if it's the group structure on the type as opposed to the type itself then in Lean it would be called _inst_1
(an auto-generated name) and humans would not have to worry about your question at all.
Bernd Losert (Dec 06 2021 at 23:00):
But classes are just structs with a class attribute, so I think the same mechanism will work for classes.
Kevin Buzzard (Dec 06 2021 at 23:02):
Yes but humans don't name instances of classes so you can't write g.to_semigroup
. Can you post some code?
Kevin Buzzard (Dec 06 2021 at 23:02):
import tactic
example (G : Type) [group G] (a b c : G) : (a * b) * c = a * (b * c) := semigroup.mul_assoc a b c
It happened by itself, no g.to_semigroup
.
Bernd Losert (Dec 06 2021 at 23:03):
You can if you're making a function where instead of [group a], you write (g : group a). I just used groups as an example. My actual working code involves convergence systems.
Kevin Buzzard (Dec 06 2021 at 23:04):
Oh sure you can override the system, I have no idea what contortions you'd have to go through then. Did you know that fields are additive monoids? I use this, but I have no idea how many .to_?
s I need to add to prove it.
Kevin Buzzard (Dec 06 2021 at 23:05):
Whyever would you want to override the type class system though?
Kevin Buzzard (Dec 06 2021 at 23:07):
import tactic
def foo (G : Type) [group G] (a b c : G) : (a * b) * c = a * (b * c) := semigroup.mul_assoc a b c
set_option pp.all true
#print foo
/-
def foo : ∀ (G : Type) [_inst_1 : group.{0} G] (a b c : G),
@eq.{1} G
(@has_mul.mul.{0} G
(@has_mul.mk.{0} G
(@semigroup.mul.{0} G
(@monoid.to_semigroup.{0} G (@div_inv_monoid.to_monoid.{0} G (@group.to_div_inv_monoid.{0} G _inst_1)))))
(@has_mul.mul.{0} G
(@has_mul.mk.{0} G
(@semigroup.mul.{0} G
(@monoid.to_semigroup.{0} G
(@div_inv_monoid.to_monoid.{0} G (@group.to_div_inv_monoid.{0} G _inst_1)))))
a
b)
c)
(@has_mul.mul.{0} G
(@mul_one_class.to_has_mul.{0} G
(@monoid.to_mul_one_class.{0} G (@div_inv_monoid.to_monoid.{0} G (@group.to_div_inv_monoid.{0} G _inst_1))))
a
(@has_mul.mul.{0} G
(@mul_one_class.to_has_mul.{0} G
(@monoid.to_mul_one_class.{0} G
(@div_inv_monoid.to_monoid.{0} G (@group.to_div_inv_monoid.{0} G _inst_1))))
b
c)) :=
λ (G : Type) [_inst_1 : group.{0} G] (a b c : G),
@semigroup.mul_assoc.{0} G
(@monoid.to_semigroup.{0} G (@div_inv_monoid.to_monoid.{0} G (@group.to_div_inv_monoid.{0} G _inst_1)))
a
b
c
-/
Looks like under the hood it goes group.to_div_inv_monoid
, div_inv_monoid.to_monoid
, monoid.to_semigroup
.
Bernd Losert (Dec 06 2021 at 23:08):
It's not about overriding, but about defining functions that don't use instance arguments but regular arguments. For example, https://leanprover-community.github.io/mathlib_docs/topology/order.html#topological_space.induced
Kyle Miller (Dec 06 2021 at 23:10):
Bernd Losert said:
But classes are just structs with a class attribute, so I think the same mechanism will work for classes.
They also have instances for all the things they extend
, making it convenient to use lemmas about, say, a semigroup, when what you have is a group.
Bernd Losert (Dec 06 2021 at 23:12):
Yes. That's true. In my case though, I'm making functions that basically producing values to use as instances, just like the topological_space.induced
function I linked above.
Kyle Miller (Dec 06 2021 at 23:15):
The tradeoff between classes and structures is that [group a]
should mean a
has a single group structure ever, but you get to more conveniently use lemmas due to typeclass search, and (g : group a)
means you can use multiple group structures on the same type and more easily compare them, but then having a hierarchy of types of structures is more inconvenient.
Topological spaces have the annoying property that most of the time a class is good, but sometimes you do want to deal with multiple topologies on the same type (there's a lattice of topologies after all), which can get difficult to manipulate.
Kevin Buzzard (Dec 06 2021 at 23:15):
The reason that induced
is a def
and not an instance
is that type class inference will never be able to find the f
so it were an instance it would never fire.
Bernd Losert (Dec 06 2021 at 23:20):
Since convergence spaces are like topological spaces, I'm going to run into all the sames issues I guess.
Bernd Losert (Dec 06 2021 at 23:20):
I'm going to basically copy/paste the topology
code in mathlib, but using convergence spaces.
Last updated: Dec 20 2023 at 11:08 UTC