Zulip Chat Archive

Stream: lean4

Topic: apply and term mode unfolding


Yakov Pechersky (Mar 24 2021 at 14:54):

In the following (somewhat minimal) mwe, the last two examples behave unexpectedly for me (at least, based on my lean3 understanding). Why would apply break but refine is fine? Why does the goal in the term mode example display the statement describing an unfolded set Le instead of a plain Le?

class HasMem (α : outParam $ Type u) (β : Type v) where
    mem : α  β  Prop

class PartialOrder (P : Type u) extends HasLessEq P where
  refl (s : P) : s  s
  antisymm (s t : P) : s  t  t  s  s = t
  trans (s t u : P) : s  t  t  u  s  u

theorem HasLessEq.LessEq.trans {P : Type _} [PartialOrder P] {x y z : P} : x  y  y  z  x  z := PartialOrder.trans _ _ _

infix:50 " ∈ " => HasMem.mem

def Set (α : Type u) := α  Prop

namespace Set

instance : HasMem α (Set α) := λ a s => s a

theorem ext {s t : Set α} (h :  x, x  s  x  t) : s = t :=
  funext $ λ x => propext $ h x

instance : HasLessEq (Set α) := λ s t =>  {x : α}, x  s  x  t

instance : PartialOrder (Set α) where
  refl := λ s x => id
  antisymm := λ s t hst hts => ext $ λ x => hst, hts
  trans := λ s t u hst htu x hxs => htu $ hst hxs

variable (x y z : Set α) (hxy : x  y) (hyz : y  z)

example : x  z := hxy.trans hyz

example : x  z := by
  refine hxy.trans ?_
  exact hyz

example : x  z := by
  apply hxy.trans -- failed to unify
  admit

example : x  y  y  z  x  z := λ h h' => _ -- goal view has the unfolded `x✝ ∈ x → x✝ ∈ z` instead of `x ≤ y`

Yakov Pechersky (Mar 24 2021 at 16:05):

The unfolded situation at the bottom example improves when we instead say

instance : HasLessEq (Set α) := λ s t =>  (x : α), x  s  x  t -- notice the explicit arg

Yakov Pechersky (Mar 24 2021 at 16:06):

So it seems like it is eagerly expanding the lambda when the lambda has an implicit arg

Kevin Buzzard (Mar 25 2021 at 21:02):

I'm also seeing this. If instance : HasLessEq (Set α) := ⟨λ s t => ∀ {a : α}, a ∈ s → a ∈ t⟩ with implicit a then goals unfold from x ⊓ y ≤ xto x✝ ∈ x ⊓ y → x✝ ∈ x and then I can't directly apply a theorem saying x ⊓ y ≤ x because I get a typeclass instance problem is stuck error; I have to explicitly tell infLeLeft that I'm talking about Set α. Conversely if I define instance : HasLessEq (Set α) := ⟨λ s t => ∀ (a : α), a ∈ s → a ∈ t⟩ with explicit a then I get extra _s everywhere (but it stops x ⊓ y ≤ x from unfolding).

Mario Carneiro (Mar 25 2021 at 21:07):

This is what {{a : A}} binders are for

Yakov Pechersky (Mar 25 2021 at 21:09):

Yes, but are those a thing in lean4?

Mario Carneiro (Mar 25 2021 at 21:10):

Just checked: no

Kevin Buzzard (Mar 25 2021 at 21:13):

Yakov just pointed out this from the manual:

Implicit lambdas
In Lean 3 stdlib, we find many instances of the dreadful @+_ idiom. It is often used when we the expected type is a function type with implicit arguments, and we have a constant (reader_t.pure in the example) which also takes implicit arguments. In Lean 4, the elaborator automatically introduces lambdas for consuming implicit arguments. We are still exploring this feature and analyzing its impact, but the experience so far has been very positive. As an example, here is the example in the link above using Lean 4 implicit lambdas.

Kevin Buzzard (Mar 25 2021 at 21:34):

OK so it's not hard to work around:

def Set α := α  Prop

class HasMem (α : outParam $ Type u) (β : Type v) where
    mem : α  β  Prop

infix:50 " ∈ " => HasMem.mem

instance : HasMem α (Set α) := λ a s => s a

instance : HasLessEq (Set α) := λ s t =>  {x : α}, x  s  x  t -- implicit seems best

class HasInf (P : Type u) where
  inf : P  P  P

infix:70 " ⊓ " => HasInf.inf

instance : HasInf (Set α) := λ s t x => x  s  x  t

theorem infLeLeft {s t : Set α} : s  t  s := And.left -- introducing implicits works great here
theorem infLeRight {s t : Set α} : s  t  t := And.right -- introducing implicits works great here

theorem inter_mem_sets_iff (f : Set (Set α)) (hf :  {s t}, s  f  s  t  t  f) :
  x  y  f  x  f  y  f := by
  refine λ h => hf h (infLeLeft), -- introducing implicits work great if you finish the job
                 hf h ?h2; -- but here I don't finish the job
  /-
    goal now not x ⊓ y ≤ y, but
    x✝ : α
    ⊢ x✝ ∈ x ⊓ y → x✝ ∈ y
    This surprised me, but now I see it's in the manual.
  -/
  -- apply infLeRight; -- fails to unify
  intro thing; -- to make apply work
  apply infLeRight thing;

Mario Carneiro (Mar 25 2021 at 21:46):

You can use @ to prevent auto-lambda on goals starting with implicits, but I don't see an easy way to make it work with apply

theorem inter_mem_sets_iff (f : Set (Set α)) (hf :  {s t}, s  f  s  t  t  f) :
  x  y  f  x  f  y  f := by
  refine λ h => hf h (infLeLeft), hf h @?h2
  -- goal now x ⊓ y ≤ y
  -- apply infLeRight -- fails to unify
  -- apply @infLeRight -- fails to unify
  refine @infLeRight _ _ _

Mario Carneiro (Mar 25 2021 at 21:47):

in particular, the fact that apply @infLeRight doesn't work seems like the latest iteration of the apply bug

Leonardo de Moura (Mar 26 2021 at 01:34):

@Yakov Pechersky @Kevin Buzzard I fixed the two problems that were creating the weird behavior. I was a bug in the apply tactic and the new implicit lambda feature. The examples above are working now.

Yakov Pechersky (Mar 26 2021 at 01:35):

Thank you for the fixes!


Last updated: Dec 20 2023 at 11:08 UTC