Zulip Chat Archive

Stream: general

Topic: Avoiding boilerplate with subclasses


Marc Masdeu (Jun 02 2022 at 14:16):

Hi,

I am trying to prepare for some geometry introduction to Lean, where students will need to prove facts about synthetic geometry, using concepts like lines, segments, rays, angles, circles, triangles,... All these have in common that they are subsets of the plane, which are "distinguished" in some way, so I decided that there would be structures corresponding to each of them, which would remember how they were created. Ideally I would redefine then the equality sign (a ray is defined by two points -it's the half-line starting at the first and containing the second point-, but the second point is not uniquely determined given a ray). If I'm not mistaken, the equality cannot be changed, so instead I go for has_equiv. Here is a mwe of what I have:

import tactic

noncomputable theory
open_locale classical

def pts {α β : Type*} [has_mem α β] (S : β) : set α := {x : α | x  S}

class IncidencePlane (Point : Type*) :=
    (Line : Type*)
  (belongs : Point  Line  Prop)
  (between : Point  Point  Point  Prop)

namespace IncidencePlane

variables (Ω : Type*) [IncidencePlane Ω]
variables (A B : Ω)

-- Stuff about Line
instance : has_mem Ω (Line Ω) := belongs
instance : has_coe (Line Ω) (set Ω) := pts

-- Stuff about Segment
structure Segment :=
(A : Ω) (B : Ω)
infix `⬝`:100 := Segment.mk

namespace Segment
instance : has_mem Ω (Segment Ω) := λ P S, between S.A P S.B
instance : has_equiv (Segment Ω) := λ S T, (S.A = T.A  S.B = T.B)  (S.A = T.B  S.B = T.A)⟩
instance Segment.has_coe_to_set : has_coe (Segment Ω) (set Ω) := pts
end Segment

-- Stuff about Ray
structure Ray (Point : Type*):=
    (origin : Point) (target : Point)
notation A `=>` B := Ray.mk A B

namespace Ray
instance : has_mem Ω (Ray Ω) := λ P r, (between r.origin P r.target)  (between r.origin  r.target  P)⟩
instance : has_equiv (Ray Ω) := λ S T, S.origin = T.origin  ((between S.origin S.target T.target)  between S.origin T.target S.target)⟩
instance has_coe_to_set : has_coe (Ray Ω) (set Ω) := pts
end Ray


#check (AB : set Ω) -- works
#check (A=>B : set Ω) -- works

end IncidencePlane

As you can see, both Segment and Rayare instances of has_mem (they are subsets of points) and have a defined notion of equivalence has_equiv. When defining a new such object, I need to write a bunch of code that deals with this. I would like to instead have them be instances of a class, say Subset Ω, so that something like this would make the last checks pass:

structure Segment (A B :Ω) extends Subset Ω :=
{
(mem : ...)
(equiv : ...)
}

and then I would be able to, given S : Segment Ω, consider S.A, S.B, and also S : set Ω. I'm sure that this is possible, I tried something like

structure Subset (Ω : Type*) :=
(mem : Ω  Prop)
instance has_mem_subset (Ω : Type*) : has_mem Ω (subset Ω) := λ P S, S.mem P
instance coe_subset_to_set (Ω : Type*) : has_coe (subset Ω) (set Ω) := pts

but I am a bit lost. Any pointers?

Thank you.

Yaël Dillies (Jun 02 2022 at 14:19):

Do you know about docs#sym2?

Marc Masdeu (Jun 02 2022 at 14:23):

That would probably help with segments, but not with the rest of distinguished subsets, right?

Marc Masdeu (Jun 02 2022 at 17:41):

Here's a more concise question. I'd like to avoid the line instance : has_coe (Segment Ω) (Subset Ω) := ⟨λ S, S.to_Subset⟩ in the code below, at the cost of adding as many lines as necessary in the Segment part.

import tactic

class IncidencePlane (Point : Type*) :=
    (Line : Type*)
  (between : Point  Point  Point  Prop)

namespace IncidencePlane

variables {Ω : Type*} [IncidencePlane Ω]
variables (A B : Ω)

class Subset (Ω : Type*) :=
(mem : Ω  Prop)
instance has_mem_subset (Ω : Type*) : has_mem Ω (Subset Ω) := λ P S, @Subset.mem Ω S P
instance coe_subset_to_set (Ω : Type*) : has_coe (Subset Ω) (set Ω) := λ S, {x : Ω | x  S}⟩

structure Segment (Ω : Type*) extends Subset Ω :=
(A : Ω) (B : Ω)
instance : has_coe (Segment Ω) (Subset Ω) := λ S, S.to_Subset

def Segment_mk (A B : Ω) : Segment Ω := { mem := λ P, between A P B,
  A := A,
  B := B }
infix `⬝`:100 := Segment_mk

#check AB -- works
#check (AB : set Ω) -- WANT THIS TO WORK

end IncidencePlane

Kevin Buzzard (Jun 02 2022 at 17:44):

It works for me in the sense that there are no errors. What do you want to happen? Output is ↑(A⬝B) : set Ω.

Marc Masdeu (Jun 02 2022 at 22:20):

@Kevin Buzzard it works as is, but I want to reduce the boilerplate needed to declare the Segment stuff. I have half a more structures like Segment (Ray, Angle, Circle, Triangle) and for each of them now I need about 6 lines of code. It seems to me that it should be doable in one line (plus one more to define notation)...


Last updated: Dec 20 2023 at 11:08 UTC