Zulip Chat Archive
Stream: new members
Topic: Idiomatic Instance Declarations
Mark Fischer (Jan 24 2024 at 14:32):
In mathlib, I've noticed that instance declarations often tend to just call separately defined functions. So instead of
instance instFooString : ToString Foo where
toString _ := "Bar"
I see something like:
def Foo.toString (_ : Foo) : String := "Bar"
instance instFooString : ToString Foo where
toString := Foo.toString
It seems like these should be roughly equivalent. Is there a pragmatic reason beyond style to prefer one over the other? My only guess is that you can save the compiler a Typeclass lookup when you write something like f.toString
to call the function directly.
Eric Wieser (Jan 24 2024 at 15:03):
Usually the first one is preferrable in mathlib
Eric Wieser (Jan 24 2024 at 15:03):
If you write the second one, you then need to write a lemma that says Foo.toString f = toString f
Eric Wieser (Jan 24 2024 at 15:03):
My only guess is that you can save the compiler a Typeclass lookup when you write something like f.toString to call the function directly.
This is a false economy because now you have a spelling of toString
that there are no theorems about!
Eric Wieser (Jan 24 2024 at 15:04):
(though toString
is a bad example; think Nat.mul m n
vs m * n
)
Mark Fischer (Jan 24 2024 at 15:07):
Nat.mul seems to follow the second style?
Mark Fischer (Jan 24 2024 at 15:12):
Or like:
def Nat.lt (n m : Nat) : Prop :=
Nat.le (succ n) m
instance : LT Nat where
lt := Nat.lt
instead of
instance : LT Nat where
lt n m := Nat.le (succ n) m
Eric Rodriguez (Jan 24 2024 at 15:25):
Treq said:
Nat.mul seems to follow the second style?
Yes, Eric is saying that it doesn't have many theorems proved about it in the form Nat.mul a b, so it's awkward to work with.
Mark Fischer (Jan 24 2024 at 15:28):
Right. Okay, I follow
Kyle Miller (Jan 24 2024 at 15:34):
There's a pragmatic reason in the generated code: the compiler usually inlines instance definitions, so having a separate definition prevents the compiler from inlining the main definition. If the definition is complicated, like a big match
expression, this can save a lot on compilation time. I had one case that improved the compilation time by around 10x if I remember correctly.
Kyle Miller (Jan 24 2024 at 15:36):
With Nat.mul
and the like, one reason I believe is to have a definition that the kernel can know the name of, since it contains code to accelerate the computation using a bignum library, rather than by evaluating it by the Lean definition.
Mark Fischer (Jan 24 2024 at 15:43):
I think my takeaway is this: I should expect to see both styles in wild. I should prefer the first style unless I have pragmatic reasons to move to the second.
Mario Carneiro (Jan 27 2024 at 07:50):
Another reason to use an instance that points at a definition is that at "instance reducibility", used by instance search, it will unfold instances but not regular definitions. So if you have a big and complicated definition then this trick will avoid it getting unfolded during instance search. This is usually a good idea once you get past the "record containing more records" stage.
Last updated: May 02 2025 at 03:31 UTC