I want to prove a property parameterized over a finite number of cases. I would like to divide the problem to one instance per case and solve each instance separately. Here is an example to clear up things:
module Minimal
open FStar.List
open FStar.Tactics
open FStar.Reflection.Data
unfold let lst = [0;1]
unfold let prop i =
match i with
| 0 -> i == 0
| 1 -> i == 1
| _ -> False
val propHolds (i:int) : Lemma (requires (List.mem i lst)) (ensures (prop i))
In this case the cases are defined by the list lst. I can easily prove propHolds:
let propHolds i =
assert_by_tactic (prop 0) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized"; trivial ());
assert_by_tactic (prop 1) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized"; trivial ())
but I obviously don't want to write a separate assert_by_tactic for each case (not when there may be thousands..). I somehow want to generate the proof above automatically for all elements in lst.
I tried various things, here is one of them:
assert_by_tactic (let rec props i =
if i = 0 then prop 0
else (prop i) /\ (props (i-1))
in
props 1) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized")
Unfortunately, this doesn't quite achieve what I would like, the assert_by_tactic fails (and is not reduced in the way I would expect). I think I am missing something obvious about normalization, but what is the canonical way to do this in F*? Bonus points if the solution points to the "case"/assertion that failed if there exists one.