Zulip Chat Archive

Stream: general

Topic: splitting decidable (a /\ b \/ c /\ ...)


Hunter Freyer (Oct 03 2020 at 14:44):

If my goal is something like decidable (a /\ (b \/ c) /\ ...), how can I quickly split that into separate goals for decidable a and decidable b \/ c, etc?

Kevin Buzzard (Oct 03 2020 at 14:45):

Try decidable_and? No idea if it's there but that's what I'd call it

Simon Hudon (Oct 03 2020 at 15:02):

Because decidable is a type class, we can find many definitions with that type using #print instances:

#print instances decidable
-- xor.decidable : Π {p q : Prop} [_inst_1 : decidable p] [_inst_2 : decidable q], decidable (xor p q)
-- iff.decidable : Π {p q : Prop} [_inst_1 : decidable p] [_inst_2 : decidable q], decidable (p ↔ q)
-- implies.decidable : Π {p q : Prop} [_inst_1 : decidable p] [_inst_2 : decidable q], decidable (p → q)
-- not.decidable : Π {p : Prop} [_inst_1 : decidable p], decidable (¬p)
-- or.decidable : Π {p q : Prop} [_inst_1 : decidable p] [_inst_2 : decidable q], decidable (p ∨ q)
-- and.decidable : Π {p q : Prop} [_inst_1 : decidable p] [_inst_2 : decidable q], decidable (p ∧ q)
-- decidable.false : decidable false
-- decidable.true : decidable true

This tells us that and.decidable is what you're looking for

Hunter Freyer (Oct 03 2020 at 18:03):

@Simon Hudon I would have thought that, but it doesn't work:

theorem foo : Π (a b : Prop), decidable (a  b) := begin
    intros,
    apply and.decidable, -- "failed to synthesize type class instance"
end

Hunter Freyer (Oct 03 2020 at 18:12):

This post from 2017 seems to indicate that you can't use decidable like that because it's a type class, not a normal predicate? But that may be outdated and/or incorrect

Simon Hudon (Oct 03 2020 at 18:16):

You can use it like this:

theorem foo : Π (a b : Prop), decidable (a  b) :=
begin
    intros,
    have h₀ : decidable a, sorry,
    have h₁ : decidable b, sorry,
    resetI,
    apply_instance,
end

Simon Hudon (Oct 03 2020 at 18:16):

resetI will add h₀ and h₁ to the instance cache

Reid Barton (Oct 03 2020 at 18:19):

decidable is a type class and meant to be used in type class-y ways. What are you really trying to do?

Reid Barton (Oct 03 2020 at 18:20):

Normally you shouldn't end up with decidable goals at all

Reid Barton (Oct 03 2020 at 18:20):

Either use classical or write the instances you need

Mario Carneiro (Oct 03 2020 at 18:20):

One trick I sometimes use when I want lean to not infer a typeclass is refine @and.decidable _ _ (id _) (id _)

Mario Carneiro (Oct 03 2020 at 18:22):

It seems like a mistake that lean will infer instances even if you write @foo ... _, but I can see that many things will break if this changes. It would be nice if there was a mode that allowed this to be "no really" explicit

Reid Barton (Oct 03 2020 at 18:23):

Lean 4 has a distinction between _ and ? (I think that's the syntax), which maybe would apply here too

Reid Barton (Oct 03 2020 at 18:23):

with one meaning "infer" and the other meaning "make a new goal"

Hunter Freyer (Oct 03 2020 at 18:27):

@Reid Barton I'm trying to show a predicate is decidable... What should the goal be, if not "decidable"?

Mario Carneiro (Oct 03 2020 at 18:28):

If so you are writing a program and probably shouldn't be using tactics until you get to the proof part

Reid Barton (Oct 03 2020 at 18:28):

then normally the proof would be some variant of by { dunfold [...], apply_instance } or use decidable_of_iff

Mario Carneiro (Oct 03 2020 at 18:29):

But if your predicate has lots of ands and ors in it you should probably be doing what Simon showed. Assuming by apply_instance alone doesn't work, add the necessary instances to the context with haveI and then use apply_instance

Mario Carneiro (Oct 03 2020 at 18:30):

this mostly only comes up for recursive predicates. For nonrecursive predicates Reid's suggestion is the best

Hunter Freyer (Oct 03 2020 at 18:30):

Here's what I'm working with:

@[derive decidable_eq]
inductive val : Type
| nat
| nil
| cons : Π (car : val) (cdr: val), val

@[derive decidable_eq]
inductive fn2 : Type
| zero : fn2
| succ : fn2
| car  : fn2
| cdr  : fn2
| nil  : fn2
| cons (car cdr : fn2) : fn2
| comp (f g : fn2) : fn2
| prec (z_case s_case : fn2) : fn2

inductive typed : fn2 -> val -> val -> Prop
| zero : typed fn2.zero val.nil val.nat
| succ : typed fn2.succ val.nat val.nat
| car {tcar tcdr : val} : typed fn2.car (val.cons tcar tcdr) tcar
| cdr {tcar tcdr : val} : typed fn2.cdr (val.cons tcar tcdr) tcdr
| nil {t: val} : typed fn2.nil t val.nil
| cons {tin tcar tcdr: val} {car cdr : fn2}
    (tcar_t : typed car tin tcar)
    (tcdr_t : typed cdr tin tcdr) :
    typed (fn2.cons car cdr) tin (val.cons tcar tcdr)
| comp {tin tint tout: val} {f g : fn2}
    (f_t : typed f tint tout)
    (g_t : typed g tin tint) :
    typed (fn2.comp f g) tin tout
| prec {trest tout: val} {z_case s_case : fn2}
    (z_t : typed z_case trest tout)
    (s_t : typed s_case (val.cons val.nat (val.cons tout trest)) tout):
    typed (fn2.prec z_case s_case) (val.cons val.nat trest) tout

mk_iff_of_inductive_prop typed typed_iff

@[instance] def typed.decidable (f : fn2) (tin tout : val) : decidable (typed f tin tout) :=
begin

Hunter Freyer (Oct 03 2020 at 18:30):

Trying to show that whether a type assignment is correct or not is decidable.

Mario Carneiro (Oct 03 2020 at 18:32):

The top level should be a recursion using the equation compiler

Hunter Freyer (Oct 03 2020 at 18:35):

writing it out in a forward way will work, but it's so duplicative of the definition of typed that it seems like metaprogramming should surely be better, right? I guess not if you say so

Mario Carneiro (Oct 03 2020 at 18:43):

Eh, actually some of this is a bit annoying, metaprogramming is better

Hunter Freyer (Oct 03 2020 at 18:43):

I don't actually know how to prove false in the cases that don't match any constructor.

Mario Carneiro (Oct 03 2020 at 18:43):

actually it is better to phrase this as a function from fn to its type

Mario Carneiro (Oct 03 2020 at 18:53):

def fn2.typecheck : fn2  val  option val
| fn2.zero val.nil := some val.nat
| fn2.succ val.nat := some val.nat
| fn2.car (val.cons tcar tcdr) := some tcar
| fn2.cdr (val.cons tcar tcdr) := some tcdr
| fn2.nil _ := some val.nil
| (fn2.cons car cdr) tin := do
  tcar  car.typecheck tin,
  tcdr  cdr.typecheck tin,
  return (val.cons tcar tcdr)
| (fn2.comp f g) tin := do
  tint  g.typecheck tin,
  f.typecheck tint
| (fn2.prec z_case s_case) (val.cons val.nat trest) := do
  tout  z_case.typecheck trest,
  tout'  s_case.typecheck (val.cons val.nat (val.cons tout trest)),
  guard (tout' = tout),
  return tout
| _ _ := none

theorem fn2.typecheck_iff {f : fn2} {tin tout : val} :
  typed f tin tout  f.typecheck tin = some tout :=
sorry

instance typed.decidable (f : fn2) (tin tout : val) : decidable (typed f tin tout) :=
decidable_of_iff' _ fn2.typecheck_iff

Mario Carneiro (Oct 03 2020 at 19:14):

theorem fn2.typecheck_iff {f : fn2} {tin tout : val} :
  typed f tin tout  f.typecheck tin = some tout :=
begin
  split,
  { intro h, induction h; try {refl};
    simp [fn2.typecheck, *, return, pure] },
  { induction f generalizing tin tout,
    iterate 5 {{ cases tin; rw typed_iff; simp [fn2.typecheck, eq_comm] }},
    { simp [fn2.typecheck],
      rintro a h1 b h2 ⟨⟩,
      constructor; solve_by_elim },
    { simp [fn2.typecheck],
      rintro a h1 h2,
      constructor; solve_by_elim },
    { cases tin with car trest; try { simp [fn2.typecheck] },
      cases car; simp [fn2.typecheck],
      rintro a h1 b h2 ⟨⟩ ⟨⟩,
      constructor; solve_by_elim } }
end

Mario Carneiro (Oct 03 2020 at 19:15):

@Hunter Freyer


Last updated: Dec 20 2023 at 11:08 UTC