Zulip Chat Archive
Stream: general
Topic: Functions that are polymorphic over a typeclass
Michael George (May 22 2024 at 15:44):
I'm trying to write a function that is polymorphic across different typeclasses, and I'm having some difficulties getting it to type properly.
I'm working on interval arithmetic, and I have a structure that wraps an exact value up with a floating point approximation for that value; the type of the exact value is polymorphic:
structure FilteredReal (α : Type) where
value : Thunk α
approx : Float
I would like to give instances for the relevant operations that just operate pointwise on the value
and approx
. For example:
instance [Add \a]: Add (FilteredReal \a) where
add x y := {
value := Thunk.mk (fun () => x.value.get + y.value.get)
approx := x.approx + y.approx
}
That works fine, but I have a large number of such instances and typos are easy, and I was wondering whether I could abstract this into something like
instance [Add \a]: Add (FilteredReal \a) := \< lift Add.add \>
I tried several variations on the following to define lift
:
def lift {CLS : {u : Nat} → Type u → Type u} [CLS Float]
(op : {β : Type u} → [CLS β] → β → β → β)
{α : Type u} [CLS α] (x y : FilteredReal α) : FilteredReal α := {
value := Thunk.mk (fun () => op x.value.get y.value.get)
range := op x.range y.range
}
However, I keep having errors with universes or invalid binders or both. For example,
argument Float has type Type : Type 1 but is expected to have type Type u : Type (u + 1)
Is it possible to write something like lift
?
Chris Bailey (May 22 2024 at 22:43):
I think for something like this, where the problem statement is "I need a bunch of repetitive code and it's going to be a nightmare" and not necessarily addressing some underlying structure, you probably want a metaprogram/tactic.
Universe issues aside, introducing lift
and making it part of your instance's definition might not end up being totally transparent when you try to prove stuff with it later. I don't know if there's a more modern way to do this, but I wrote something in a similar vein to implement typeclasses for the fixed with integer types here, you should be able to use something very similar.
Chris Bailey (May 22 2024 at 22:49):
Just spit-balling, but something like this should work:
set_option hygiene false in
run_cmd
for (typeName, methodName) in [(`Add, `add), ..].map (fun pr => (Lean.mkIdent pr.fst, Lean.mkIdent pr.snd)) do
Lean.Elab.Command.elabCommand (← `(
instance [$typeName A] : $typeName (FilteredReal A) where
$methodName a b := {
..
}
...
Eric Wieser (May 23 2024 at 08:07):
This is also a place that we could have a derive
handler
Andrés Goens (May 23 2024 at 09:03):
Eric Wieser said:
This is also a place that we could have a
derive
handler
how difficult is that to implement directly ourselves (in user code)? is that documented somewhere?
Marcus Rossel (May 23 2024 at 15:01):
@Andrés Goens, this is an example of the scaffolding you need: https://github.com/leanprover/lean4/blob/master/src/Lean/Elab/Deriving/SizeOf.lean
Last updated: May 02 2025 at 03:31 UTC