open tactic list expr

private meta definition pattern_telescope : expr → list expr → tactic (list expr × expr × expr)
| e ps :=
if expr.is_pi e = tt then do
   n      ← mk_fresh_name,
   p      ← return $ local_const n (binding_name e) (binding_info e) (binding_domain e),
   new_e  ← return $ instantiate_var (binding_body e) p,
   pattern_telescope new_e (p :: ps)
else do
  (lhs, rhs) ← match_eq e,
  return (reverse ps, lhs, rhs)

meta definition mk_pattern_for_constant : name → tactic pattern
| n :=
do env ← get_env,
   d   : declaration  ← returnex $ environment.get env n,
   ls  : list level   ← return $ map level.param (declaration.univ_params d),
   (some type)        ← return $ declaration.instantiate_type_univ_params d ls | failed,
   (es, lhs, rhs)     ← pattern_telescope type [],
   p   : pattern      ← mk_pattern ls es lhs [] [rhs, app_of_list (expr.const n ls) es],
   return p

open nat

constant add.comm (a b : nat) : a + b = b + a

example (a b : nat) (H : a + b + a + b = 0) : true :=
by do
  a ← get_local `a, b ← get_local `b,
  H ← get_local `H >>= infer_type,
  (lhs, rhs) ← match_eq H,
  p ← mk_pattern_for_constant $ `add.comm,
  [rhs_inst, prf] ← match_pattern p lhs | failed,
  trace "match rhs",
  trace rhs_inst,
  trace "proof lhs = rhs",
  trace prf,
  prf_type ← infer_type prf,
  trace "proof type:",
  trace prf_type,
  constructor
