Zulip Chat Archive
Stream: lean4
Topic: Inheritance question
Patrick Massot (Sep 19 2023 at 16:21):
I have a very basic question but I'm not sure about the idiomatic solution. Maybe I'm too much used to python, but I want to be able to write a function taking as argument any object inheriting from some basic class. Ideally I would like to write
structure Enough where
x : Nat
y : Nat
and then define a function
def getX {α : Type _} "some way to state that α is a structure extending Enough" (a : α) : Nat := a.x
That could be applied to x : Extra
where
structure Extra where
x : Nat
y : Nat
z : Nat
I know I can use
class Enough (α : Type) where
x : α → Nat
y : α → Nat
def getX [Enough α] (a : α) : Nat := Enough.x a
structure Extra where
x : Nat
y : Nat
z : Nat
instance : Enough (Extra) where
x := Extra.x
y := Extra.y
#eval getX (Extra.mk 1 2 3)
But this doesn't feel right. What am I missing?
Eric Wieser (Sep 19 2023 at 16:22):
I think that's the idiomatic solution, as it's what we use to generalize over subobjects and morphisms
Eric Wieser (Sep 19 2023 at 16:22):
What you (and everyone else) are missing is automation to build this automatically
Eric Wieser (Sep 19 2023 at 16:24):
Your alternative is to exploit dot notation "seeing through" extends
:
structure Enough where
x : Nat
y : Nat
def Enough.getX (a : Enough) : Nat := a.x
structure Extra extends Enough where
z : Nat
#eval ({x := 1, y := 2, z := 3} : Extra).getX
Patrick Massot (Sep 19 2023 at 16:25):
Yes, this is inspired by what we do in algebraic situations, but somehow it feels weirder when Extra
has no parameter at all.
Eric Wieser (Sep 19 2023 at 16:25):
These correspond roughly to what C++ calls dynamic polymorphism and static polymorphism, respectively
Eric Wieser (Sep 19 2023 at 16:26):
I'm curious how Rust and Haskell solve this
Patrick Massot (Sep 19 2023 at 16:28):
I have a hard time deciding whether I should use the dot notation trick. It really feels dirty.
Eric Wieser (Sep 19 2023 at 16:28):
It also breaks Extra.mk 1 2 3
, though you can fix that by writing Extra.mk {} 1 2 3
and using the FlatHack
trick from my CICM paper
Patrick Massot (Sep 19 2023 at 16:30):
Currently in my code I use the solution I showed at the beginning of this thread, with a ugly derive handler.
Patrick Massot (Sep 19 2023 at 16:30):
And I really mean ugly: https://github.com/leanprover-community/mathlib4/blob/pm_widgets/Mathlib/Tactic/Widget/SelectInsertParamsClass.lean
Patrick Massot (Sep 19 2023 at 16:31):
It does the job at https://github.com/leanprover-community/mathlib4/blob/pm_widgets/Mathlib/Tactic/Widget/Calc.lean#L50-L54 but don't dare PRing this.
Eric Wieser (Sep 19 2023 at 16:34):
What's the getX
in your application?
Eric Wieser (Sep 19 2023 at 16:34):
Eric Wieser said:
I'm curious how Rust and Haskell solve this
It looks like Rust does the same thing as what you did above (their trait
is our class
/instance
)
Patrick Massot (Sep 19 2023 at 16:37):
Eric Wieser said:
What's the
getX
in your application?
There are simply the fields of SelectInsertParamsClass
.
Patrick Massot (Sep 19 2023 at 16:38):
So my example would be closer to my actual code as:
class Enough (α : Type) where
x : α → Nat
y : α → Nat
structure Extra where
x : Nat
y : Nat
z : Nat
instance : Enough (Extra) where
x := Extra.x
y := Extra.y
#eval Enough.x (Extra.mk 1 2 3)
Patrick Massot (Sep 19 2023 at 16:46):
I think the dot notation trick doesn't work anyway, it does not allow to assign a type to functions that would need to use this trick.
James Gallicchio (Sep 19 2023 at 16:48):
I think extends
generates a Coe
instance, doesn't it? Could you use Coe A Enough
?
Eric Wieser (Sep 19 2023 at 16:48):
I think the dot notation trick doesn't work anyway, it does not allow to assign a type to functions that would need to use this trick.
These functions would just consume an Enough
(the base structure)
Matthew Ballard (Sep 19 2023 at 16:49):
I would have reached for a coercion first also
Patrick Massot (Sep 19 2023 at 16:50):
Eric Wieser said:
I think the dot notation trick doesn't work anyway, it does not allow to assign a type to functions that would need to use this trick.
These functions would just consume an
Enough
(the base structure)
I need them to consume more general arguments, otherwise there would be no discussion.
Eric Wieser (Sep 19 2023 at 16:59):
You could also consider
def Enough where
x : Nat
y : Nat
class ToEnough (α : Type) where
toEnough : α → Enough
structure Extra where
x : Nat
y : Nat
z : Nat
instance : Enough (Extra) where
toEnough e := { e with }
#eval Enough.x (ToEnough.toEnough <| Extra.mk 1 2 3)
Patrick Massot (Sep 19 2023 at 17:00):
Indeed that also works, but still feels clunky (and requires a derive handler too).
James Gallicchio (Sep 19 2023 at 17:02):
James Gallicchio said:
I think
extends
generates aCoe
instance, doesn't it? Could you useCoe A Enough
?
maybe it should autogenerate a Coe
instance...... but that's something that would require core changes, I assume
Eric Wieser (Sep 19 2023 at 17:11):
I don't think automatic generation of Coe
is a good idea
James Gallicchio (Sep 19 2023 at 17:13):
Then a To A B
class that is isomorphic to Coe B A
but without the transitivity and notational baggage :)
Last updated: Dec 20 2023 at 11:08 UTC