Zulip Chat Archive

Stream: general

Topic: simp@


Kyle Miller (Dec 16 2021 at 23:18):

Sometimes simp lemmas need local assumptions for applicability, and what I've usually done is add them to the simp set manually. I remembered today that there's also the discharger option, which you can supply with a tactic that's run to deal with extra goals that crop up.

-- fails
example (n m m' : ) (h : n  0) : m * n / n = m := by simp

-- succeeds
example (n m m' : ) (h : n  0) : m * n / n = m := by simp [h]
example (n m m' : ) (h : n  0) : m * n / n = m := by simp { discharger := tactic.assumption }

@Scott Kovach and I thought it might be interesting if this were more accessible, so we added a @ flag to join ! and ?:

-- succeeds
example (n m m' : ) (h : n  0) : m * n / n = m := by mysimp@

In addition to setting the discharger, it also sets contextual := tt, since these are sort of like additional assumptions.

Is this a good idea to have in simp? Or is it just a step down the road of inscrutability?

Kyle Miller (Dec 16 2021 at 23:18):

Implementation:

namespace tactic
namespace interactive
setup_tactic_parser

@[derive [has_reflect, decidable_eq]]
inductive mysimp_flags
| use_iota_eqn
| discharge_assumptions
| trace_lemmas

meta def mysimp_flags.apply : mysimp_flags  simp_config_ext  simp_config_ext
| mysimp_flags.use_iota_eqn cfg :=
  { iota_eqn := tt, ..cfg }
| mysimp_flags.discharge_assumptions cfg :=
  { contextual := tt, discharger := tactic.assumption <|> cfg.discharger, ..cfg }
| mysimp_flags.trace_lemmas cfg :=
  { trace_lemmas := tt, ..cfg }

meta def mysimp
  (flags : parse $
    ((with_desc "!" $ tk "!" >> pure mysimp_flags.use_iota_eqn)
    <|> (with_desc "@" $ tk "@" >> pure mysimp_flags.discharge_assumptions)
    <|> (with_desc "?" $ tk "?" >> pure mysimp_flags.trace_lemmas))*)
  (no_dflt : parse only_flag) (hs : parse simp_arg_list) (attr_names : parse with_ident_list)
              (locat : parse location) (cfg : simp_config_ext := {}) : tactic unit :=
let cfg := flags.foldr mysimp_flags.apply cfg in
propagate_tags $
do lms  simp_core cfg.to_simp_config cfg.discharger no_dflt hs attr_names locat,
  if cfg.trace_lemmas then
    let a := if flags.mem mysimp_flags.discharge_assumptions then "@" else ""
    in trace (("Try this: mysimp" ++ a ++ " only ") ++ to_fmt lms.to_list) else skip

end interactive
end tactic

Eric Wieser (Dec 16 2021 at 23:20):

Is simp { discharger := tactic.assumption } different to simp [*]?

Kyle Miller (Dec 16 2021 at 23:30):

@Eric Wieser Good point, I didn't realize [*] and simp only interacted correctly. (Or rather, I think I might have forgotten about [*].)

Kyle Miller (Dec 16 2021 at 23:31):

I'm not sure what the differences between simp {discharger := tactic.assumption} and simp [*] might be, if any.

Gabriel Ebner (Dec 20 2021 at 08:48):

IIRC simp uses simp as the default discharger, so simp [*] will usually prove a bit more than simp {discharger:=assumption}.

Kyle Miller (Dec 20 2021 at 20:10):

@Gabriel Ebner As I understand it, simp effectively uses discharger <|> simp to discharge goals, and since the default value for discharger is tactic.fail it generally works the way you describe, though simp [*] {discharger := tactic.assumption} should prove at least as much as simp [*] (and possibly exactly as much).


Last updated: Dec 20 2023 at 11:08 UTC