Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: has_lt for small inductive types


Johan Commelin (Oct 28 2020 at 09:24):

I have an inductive type

@[derive [decidable_eq, has_reflect, inhabited]]
inductive op | mul | add | div | sub | inv | neg

Is there an easy way to get a decidable has_lt on this type. I really don't care what the ordering is.

Johan Commelin (Oct 28 2020 at 09:24):

Reason: I want to use this as part of a key for a lookup table.

Johan Commelin (Oct 28 2020 at 09:25):

And rb_lmap wants a decidable has_lt on its key

Kevin Buzzard (Oct 28 2020 at 09:26):

does it have to be an order? Can you just define it to be "always false"?

Mario Carneiro (Oct 28 2020 at 09:28):

not fully automatic, but fairly easy:

namespace op
def to_nat : op  
| mul := 0 | add := 1 | div := 2
| sub := 3 | inv := 4 | neg := 5
end op

instance : has_lt op := λ x y, x.to_nat < y.to_nat

Johan Commelin (Oct 28 2020 at 09:33):

@Kevin Buzzard I wouldn't be surprised if rb_lmap will be angry with me if I try that trick.

Johan Commelin (Oct 28 2020 at 09:36):

@Mario Carneiro thx! How do I (easily) lift the decidability?

Mario Carneiro (Oct 28 2020 at 09:37):

unfold; apply_instance

Mario Carneiro (Oct 28 2020 at 09:39):

instance op.decidable_lt : decidable_rel ((<) : op  op  Prop) :=
by unfold has_lt.lt; apply_instance

Johan Commelin (Oct 28 2020 at 09:40):

instance : decidable_linear_order op :=
decidable_linear_order.lift to_nat (by { rintros ⟨⟩ ⟨⟩ ⟨⟩; refl })

Mario Carneiro (Oct 28 2020 at 09:41):

Unfortunately, it's not trivial to show that the decidable_eq instance induced by the order is the same as the decidable_eq instance you derived

Johan Commelin (Oct 28 2020 at 09:43):

Then I'll remove the derive

Mario Carneiro (Oct 28 2020 at 09:44):

actually I take it back, that rintros is doing more work for you than I thought

instance : decidable_linear_order op :=
{ decidable_eq := op.decidable_eq,
  ..decidable_linear_order.lift to_nat (by { rintros ⟨⟩ ⟨⟩ ⟨⟩; refl }) }

Johan Commelin (Oct 28 2020 at 09:45):

Is there benefit in using the decidable_eq from the derive handler?

Johan Commelin (Oct 28 2020 at 10:03):

I have two list names. How can I make lean print the diff?

Johan Commelin (Oct 28 2020 at 10:03):

meta def check_list : tactic unit :=
do env  tactic.get_env,
  let lems := (env.get_decls.map declaration.to_name).filter (λ n, name.is_prefix_of `baby_calc n),
  let M1 : multiset name := lookup.values,
  let M2 : multiset name := lems,
  -- what goes here?

Johan Commelin (Oct 28 2020 at 10:03):

I turned them into multisets because I don't care about the order.

Johan Commelin (Oct 28 2020 at 10:03):

(I also don't care about multiplicity, actually)

Mario Carneiro (Oct 28 2020 at 10:04):

that makes it difficult to print them though

Mario Carneiro (Oct 28 2020 at 10:04):

because printing requires an order

Mario Carneiro (Oct 28 2020 at 10:05):

so you can either sort them, don't turn them into multisets, or use unquot to unsafely get the underlying list

Johan Commelin (Oct 28 2020 at 10:05):

Ok, so I can take the unquot of M1 - M2.

Johan Commelin (Oct 28 2020 at 10:05):

But then, how do I print it?

Mario Carneiro (Oct 28 2020 at 10:05):

trace

Johan Commelin (Oct 28 2020 at 10:08):

type mismatch at application
  trace (quot.unquot D1)
term
  quot.unquot D1
has type
  list name
but is expected to have type
  string

Johan Commelin (Oct 28 2020 at 10:12):

type mismatch at application
  bind (trace (to_string (quot.unquot D1)))
term
  trace (to_string (quot.unquot D1))
has type
  thunk ?m_1  ?m_1 : Type ?
but is expected to have type
  tactic ?m_1 : Type

Johan Commelin (Oct 28 2020 at 10:12):

/me is bad at writing meta code

Mario Carneiro (Oct 28 2020 at 10:13):

tactic.trace

Johan Commelin (Oct 28 2020 at 10:15):

Aah, thx

Mario Carneiro (Oct 28 2020 at 10:15):

there is also _root_.trace which is useful for tracing pure code; it has a string argument that will be traced (you have to call to_string), followed by a thunk which is whatever the expression is actually supposed to return. The tactic tactic.trace is a bit easier to use, it takes anything that implements has_to_tactic_format which is just about everything

Johan Commelin (Oct 28 2020 at 10:24):

Thanks for that explanation!

Johan Commelin (Oct 28 2020 at 10:28):

Ok, I have a partial victory:

meta def check_list : tactic unit :=
do env  get_env,
  let lems := (env.get_decls.map declaration.to_name).filter (λ n, name.is_prefix_of `calc_step n),
  let M1 : multiset name := lookup.values.map (name.append `calc_step),
  let M2 : multiset name := lems,
  let D1 := (M1 - M2),
  let D2 := (M2 - M1),
  trace "diff 1:",
  trace D1.unquot,
  trace "diff 2:",
  trace D2.unquot

run_cmd check_list

Johan Commelin (Oct 28 2020 at 10:28):

The first difference is now empty

Johan Commelin (Oct 28 2020 at 10:28):

But the second one contains lots of auto-generated declerations

Johan Commelin (Oct 28 2020 at 10:28):

Can I somehow check whether a declaration was declared by me in the file using lemma, or auto-generated by lean?

Johan Commelin (Oct 28 2020 at 10:42):

private
meta def check_list : tactic unit :=
do env  get_env,
  let names := env.get_decls.map declaration.to_name,
  let in_scope := names.filter (λ n, name.is_prefix_of `calc_step n),
  let lems := in_scope.filter (λ n,
            !name.is_prefix_of `calc_step.side n &&
            !name.is_prefix_of `calc_step.op   n &&
            !name.is_prefix_of `calc_step.sign n &&
            !name.is_prefix_of `calc_step.rel  n &&
            !name.is_prefix_of `calc_step.lookup_list n &&
            !name.is_prefix_of `calc_step.lookup n),
  let M1 : multiset name := lookup.values.map (name.append `calc_step),
  let M2 : multiset name := lems,
  let D1 := (M1 - M2),
  let D2 := (M2 - M1),
  trace D1.unquot,
  trace D2.unquot

run_cmd check_list

Johan Commelin (Oct 28 2020 at 10:42):

This now traces [] [] :tada:
Thanks for all the help


Last updated: Dec 20 2023 at 11:08 UTC