While there is no built-in tactic or combinator available, you can implement one yourself as follows:
ML {*
fun solved_tac thm =
if nprems_of thm = 0 then Seq.single thm else Seq.empty
*}
method_setup solved = {*
Scan.succeed (K (SIMPLE_METHOD solved_tac))
*}
This creates a new method solved
that will succeed if the current goal has been completely solved, or fail if there is one or more subgoals still remaining.
It can be used, for example, as follows:
lemma "a ∨ ¬ a "
apply ((rule disjI1, solved) | (simp, solved))
done
Without the solved
clause, Isabelle will select the rule disjI1
side of the apply
step, leaving you with an unsolvable goal. With the solved
clause in each side, Isabelle attempts to use rule disjI1
and, when it fails to solve the goal, switches to the simp
which is then successful.
This can be used to solve individual goals by using Isabelle's (...)[1]
syntax. For example, the following statement will attempt to solve as many subgoals as possible using simp
, but will leave a subgoal unchanged if simp
fails to completely solve it:
apply ((simp, solved)[1])+