Zulip Chat Archive
Stream: lean4
Topic: working with universal typeclass instances
Colin S. Gordon (Oct 13 2022 at 14:51):
I'm fiddling around with typeclasses in Lean 4, and recently (via Lars König's very cool MS thesis learned about Lean 4's support for universal quantification in typeclass constraints. I'm fiddling with this, and I've found a case where I suspect there's a way to make something work, but I'm not having any luck getting it to work.
As a simple example, consider
class isNum (i:Nat) where
p : even i ∨ odd i
class hasP (P:Nat -> Prop) where
p : ∀ i, P i
instance hasNum : [∀ i, isNum i] -> hasP (fun x => even x ∨ odd x) where
p := by intro i
apply isNum.p
Here the universal version of isNum
is automatically found and instantiated in tactic mode when using isNum.p
. But this seems to only work because the i
from the the type of hasP.p
is in the tactic context explicitly. If I try a slight variation on this:
class proveUniv (P:Nat -> Prop) where
p := Prop
instance hasUniv : [∀ i, isNum i] -> proveUniv (fun x => even x ∨ odd x) where
p := (∀ (y:Nat), isNum.p (i:=y)) -- <-- Doesn't check
I cannot get anything I've tried for this last instance body to work. Because the universal typeclass constraint is after the colon, I can't name it and refer back to it (I can name it, but I can't use the name in the term given for hasUniv.p
). Moving it before the colon (i.e., instance hasUniv [inst:∀ i, isNum i] : provUniv ...
) results in a complaint that there is no forward dependency on i
. Attempting to stick a by
tactic block inside the ∀ (y:Nat), ...
body doesn't seem to get anything working.
It seems the issue is trying to use this universal instance under a new binder introduced in the typeclass member definition. Is this expected to work via some means I have yet to figure out? Or is there a reason this shouldn't/can't work (either an engineering reason or a theoretical reason)?
Sebastian Ullrich (Oct 13 2022 at 15:33):
Do you have a self-contained version? even
and odd
are missing, and if I add them I get "type expected" at the last instance, which makes sense because isNum.p
is a proof, while a Type
is expected at that position. I can't reproduce the forward dependency error either.
Colin S. Gordon (Oct 13 2022 at 15:51):
oops, sorry, I had forgotten I'd defined these rather than importing them
def odd (n:Nat) : Bool :=
match n with
| Nat.zero => false
| Nat.succ Nat.zero => true
| Nat.succ (Nat.succ x) => odd x
def even (n:Nat) : Bool :=
match n with
| Nat.zero => true
| Nat.succ Nat.zero => false
| Nat.succ (Nat.succ x) => even x
Colin S. Gordon (Oct 13 2022 at 15:54):
I had forgotten I'd hit this with booleans, but this variant that sticks to Prop
has the same symptoms
def fakeEven (i:Nat) : Prop := True
def fakeOdd (i:Nat) : Prop := False
class isNum (i:Nat) where
p : fakeEven i ∨ fakeOdd i
class hasP (P:Nat -> Prop) where
p : ∀ i, P i
instance hasNum : [∀ i, isNum i] -> hasP (fun x => fakeEven x ∨ fakeOdd x) where
p := by intro i
apply isNum.p -- <-- works
class proveUniv (P:Nat -> Prop) where
p := Prop
instance hasUniv : [∀ i, isNum i] -> proveUniv (fun x => fakeEven x ∨ fakeOdd x) where
p := (∀ (y:Nat), isNum.p (i:=y)) -- <-- fails
Colin S. Gordon (Oct 13 2022 at 15:56):
...and curiously I can no longer reproduce the complaint about no forward dependencies either
Sebastian Ullrich (Oct 13 2022 at 16:07):
I still get only "type expected" with that. I suspect you meant p : Prop
in proveUniv
, but even then isNum.p
still isn't a Prop
regardless of typeclass resolution.
Colin S. Gordon (Oct 13 2022 at 17:08):
ah, you're right, that's the heart of the issue here... I may have mis-distilled my minimal example
Last updated: Dec 20 2023 at 11:08 UTC