Zulip Chat Archive

Stream: general

Topic: Equation compiler _main function


Fabian Glöckle (Mar 04 2020 at 16:53):

Hello everyone,
I am defining a recursive property on expr(precisely, containing something as a subexpression) and want to show it is decidable. In a simplified (dysfunctional) version:

meta def expr.contains (x : expr) : expr  Prop
| (app fn arg) := (expr.contains fn)  (expr.contains arg)
| _ := false

meta instance expr.decidable_contains (x : expr) :  (y : expr), decidable (expr.contains x y)
| (app fn arg) := match (expr.decidable_contains fn, expr.decidable_contains arg) with
                  | (is_true h₁, _) := is_true (or.inl h₁)
                  | (_, is_true h₂) := is_true (or.inr h₂)
                  | (is_false h₁, is_false h₂) := is_false (not_or h₁ h₂)
                  end
| _ := is_false not_false

This gives the following error:

type mismatch at application
  or.inl h₁
term
  h₁
has type
  expr.contains x fn
but is expected to have type
  [expr.contains._main] fn

What is the mistake? It seems to me that the function _main defined by the equation compiler should not leak through at this point, should it?

Anne Baanen (Mar 04 2020 at 16:56):

This seems suspiciously like the issue I'm debugging. Could you write the following lines above your definition and paste the output?

set_option trace.type_context.smart_unfolding true
set_option trace.type_context.is_def_eq_detail true

Fabian Glöckle (Mar 04 2020 at 17:02):

For smart_unfolding:

[type_context.smart_unfolding] using simple unfolding [1]
opt_param bool tt
[type_context.smart_unfolding] using simple unfolding [1]
expr.contains x fn
[type_context.smart_unfolding] using simple unfolding [1]
expr.contains._main fn
[type_context.smart_unfolding] using simple unfolding [1]
expr.cases_on fn (λ (a : ), id_rhs Prop false) (λ (a : level), id_rhs Prop false)
  (λ (a_a : name) (a_a_1 : list level), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_type : expr), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_bi : binder_info) (a_type : expr), id_rhs Prop false)
  (λ (a_a a_a_1 : expr), id_rhs Prop ([expr.contains._main] a_a  [expr.contains._main] a_a_1))
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_type a_assignment a_body : expr), id_rhs Prop false)
  (λ (a_a : macro_def) (a_a_1 : list expr), id_rhs Prop false)
[type_context.smart_unfolding] using simple unfolding [1]
expr.contains x (app fn arg)
[type_context.smart_unfolding] using simple unfolding [1]
expr.contains._main (app fn arg)
[type_context.smart_unfolding] using simple unfolding [1]
expr.cases_on (app fn arg) (λ (a : ), id_rhs Prop false) (λ (a : level), id_rhs Prop false)
  (λ (a_a : name) (a_a_1 : list level), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_type : expr), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_bi : binder_info) (a_type : expr), id_rhs Prop false)
  (λ (a_a a_a_1 : expr), id_rhs Prop ([expr.contains._main] a_a  [expr.contains._main] a_a_1))
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_type a_assignment a_body : expr), id_rhs Prop false)
  (λ (a_a : macro_def) (a_a_1 : list expr), id_rhs Prop false)
[...]

Anne Baanen (Mar 04 2020 at 17:07):

Aha, I think I see what is going on: expr.contains x (app fn arg) should be directly unfolded to (expr.contains fn) ∨ (expr.contains arg), but instead it is unfolded with simple unfolding, going via expr.contains._main, so you end up with (expr.contains._main fn). And expr.contains fn does not match any of the cases, so it is not unfolded any further.

Floris van Doorn (Mar 04 2020 at 17:08):

The problem here is that expr.contains is a meta definition, and that for meta definitions, the right equations are not generated.

If I write this:

meta instance expr.decidable_contains (x : expr) :  (y : expr), decidable (expr.contains x y)
| (app fn arg) := match (expr.decidable_contains fn, expr.decidable_contains arg) with
                  | (is_true h₁, _) := is_true begin unfold expr.contains, end
                  | (_, is_true h₂) := is_true (or.inr h₂)
                  | (is_false h₁, is_false h₂) := is_false (not_or h₁ h₂)
                  end
| _ := is_false not_false

then we see the error message

unfold tactic failed, expr.contains does not have equational lemmas nor is a projection

If I make everything non-meta, then the error disappears.

inductive my_expr
| var      {} : nat  my_expr
| mvar        : name  name  my_expr  my_expr
| local_const : name  name  binder_info  my_expr  my_expr
| app         : my_expr  my_expr  my_expr
| lam         : name  binder_info  my_expr  my_expr  my_expr
| pi          : name  binder_info  my_expr  my_expr  my_expr
| elet        : name  my_expr  my_expr  my_expr  my_expr

open my_expr

def my_expr.contains (x : my_expr) : my_expr  Prop
| (app fn arg) := my_expr.contains fn  my_expr.contains arg
| _ := false

instance my_expr.decidable_contains (x : my_expr) :  (y : my_expr), decidable (my_expr.contains x y)
| (app fn arg) := match (my_expr.decidable_contains fn, my_expr.decidable_contains arg) with
                  | (is_true h₁, _) := is_true (or.inl h₁)
                  | (_, is_true h₂) := is_true (or.inr h₂)
                  | (is_false h₁, is_false h₂) := is_false (not_or h₁ h₂)
                  end
| _ := is_false sorry

Lean doesn't really support proving anything about meta defs.

Reid Barton (Mar 04 2020 at 17:09):

The Coq users will say you should just write meta def expr.contains (x : expr) : expr -> bool in the first place

Fabian Glöckle (Mar 04 2020 at 17:10):

For is_def_eq_detail:

[...]
[type_context.is_def_eq_detail] [1]: expr.contains x fn =?= [expr.contains._main] fn
[type_context.is_def_eq_detail] unfold left: expr.contains
[type_context.is_def_eq_detail] [2]: expr.contains._main fn =?= [expr.contains._main] fn
[type_context.is_def_eq_detail] unfold left: expr.contains._main
[type_context.is_def_eq_detail] [3]: expr.cases_on fn (λ (a : ), id_rhs Prop false) (λ (a : level), id_rhs Prop false)
  (λ (a_a : name) (a_a_1 : list level), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_type : expr), id_rhs Prop false)
  (λ (a_unique a_pretty : name) (a_bi : binder_info) (a_type : expr), id_rhs Prop false)
  (λ (a_a a_a_1 : expr), id_rhs Prop ([expr.contains._main] a_a  [expr.contains._main] a_a_1))
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false)
  (λ (a_var_name : name) (a_type a_assignment a_body : expr), id_rhs Prop false)
  (λ (a_a : macro_def) (a_a_1 : list expr), id_rhs Prop false) =?= [expr.contains._main] fn
[type_context.is_def_eq_detail] after whnf_core: expr.rec (λ (a : ), (λ (a : ), id_rhs Prop false) a) (λ (a : level), (λ (a : level), id_rhs Prop false) a)
  (λ (a : name) (a_1 : list level), (λ (a_a : name) (a_a_1 : list level), id_rhs Prop false) a a_1)
  (λ (unique pretty : name) (type : expr) (ih : (λ (a : expr), Prop) type),
     (λ (a_unique a_pretty : name) (a_type : expr), id_rhs Prop false) unique pretty type)
  (λ (unique pretty : name) (bi : binder_info) (type : expr) (ih : (λ (a : expr), Prop) type),
     (λ (a_unique a_pretty : name) (a_bi : binder_info) (a_type : expr), id_rhs Prop false) unique pretty bi type)
  (λ (a a_1 : expr) (ih_a : (λ (a : expr), Prop) a) (ih_a : (λ (a : expr), Prop) a_1),
     (λ (a_a a_a_1 : expr), id_rhs Prop ([expr.contains._main] a_a  [expr.contains._main] a_a_1)) a a_1)
  (λ (var_name : name) (bi : binder_info) (var_type body : expr) (ih_var_type : (λ (a : expr), Prop) var_type)
   (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false) var_name bi var_type
       body)
  (λ (var_name : name) (bi : binder_info) (var_type body : expr) (ih_var_type : (λ (a : expr), Prop) var_type)
   (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false) var_name bi var_type
       body)
  (λ (var_name : name) (type assignment body : expr) (ih_type : (λ (a : expr), Prop) type)
   (ih_assignment : (λ (a : expr), Prop) assignment) (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_type a_assignment a_body : expr), id_rhs Prop false) var_name type assignment body)
  (λ (a : macro_def) (a_1 : list expr), (λ (a_a : macro_def) (a_a_1 : list expr), id_rhs Prop false) a a_1)
  fn =?= [expr.contains._main] fn
[type_context.is_def_eq_detail] [4]: expr.rec =?= [expr.contains._main]
[type_context.is_def_eq_detail] on failure: expr.rec =?= [expr.contains._main]
[type_context.is_def_eq_detail] on failure: expr.rec (λ (a : ), (λ (a : ), id_rhs Prop false) a) (λ (a : level), (λ (a : level), id_rhs Prop false) a)
  (λ (a : name) (a_1 : list level), (λ (a_a : name) (a_a_1 : list level), id_rhs Prop false) a a_1)
  (λ (unique pretty : name) (type : expr) (ih : (λ (a : expr), Prop) type),
     (λ (a_unique a_pretty : name) (a_type : expr), id_rhs Prop false) unique pretty type)
  (λ (unique pretty : name) (bi : binder_info) (type : expr) (ih : (λ (a : expr), Prop) type),
     (λ (a_unique a_pretty : name) (a_bi : binder_info) (a_type : expr), id_rhs Prop false) unique pretty bi type)
  (λ (a a_1 : expr) (ih_a : (λ (a : expr), Prop) a) (ih_a : (λ (a : expr), Prop) a_1),
     (λ (a_a a_a_1 : expr), id_rhs Prop ([expr.contains._main] a_a  [expr.contains._main] a_a_1)) a a_1)
  (λ (var_name : name) (bi : binder_info) (var_type body : expr) (ih_var_type : (λ (a : expr), Prop) var_type)
   (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false) var_name bi var_type
       body)
  (λ (var_name : name) (bi : binder_info) (var_type body : expr) (ih_var_type : (λ (a : expr), Prop) var_type)
   (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_bi : binder_info) (a_var_type a_body : expr), id_rhs Prop false) var_name bi var_type
       body)
  (λ (var_name : name) (type assignment body : expr) (ih_type : (λ (a : expr), Prop) type)
   (ih_assignment : (λ (a : expr), Prop) assignment) (ih_body : (λ (a : expr), Prop) body),
     (λ (a_var_name : name) (a_type a_assignment a_body : expr), id_rhs Prop false) var_name type assignment body)
  (λ (a : macro_def) (a_1 : list expr), (λ (a_a : macro_def) (a_a_1 : list expr), id_rhs Prop false) a a_1)
  fn =?= [expr.contains._main] fn
[...]

Anne Baanen (Mar 04 2020 at 17:11):

If I understand the elaborator correctly, you might still be able make your original definition work by first rewriting using a lemma of the form

meta lemma trick_the_unfolder : expr.contains = expr.contains._main := rfl

(since non-applied recursive definitions don't use the smart rewriter)

Fabian Glöckle (Mar 04 2020 at 17:12):

I see, thank you all

Fabian Glöckle (Mar 04 2020 at 17:15):

So I assume if I want to use list.anyon this, I will directly make it a -> bool function instead of proving decidabilty (like the Coq user says :D ).

Mario Carneiro (Mar 04 2020 at 20:29):

Reid Barton said:

The Coq Lean 4 users will say you should just write meta def expr.contains (x : expr) : expr -> bool in the first place

I fixed your quote. I think the stance on this has changed in lean 4


Last updated: Dec 20 2023 at 11:08 UTC