10

In an Isabelle theory file, I can write simple one-line tactics such as the following:

apply (clarsimp simp: split_def split: prod.splits)

I find, however, when I start writing ML code to automate proofs, to produce an ML tactic object, these one-liners become rather verbose:

clarsimp_tac (Context.proof_map (
    Simplifier.map_ss (fold Splitter.add_split @{thms prod.splits})
    #> Simplifier.map_ss (fn ss => ss addsimps [@{thm split_def}]))
  @{context}) 1

Is there an easier way to write the simple one-line tactics at the Isabelle/ML level?

For example, something like an anti-quotation: @{tactic "clarsimp simp: split_def split: prod.splits"} producing a function of type context -> tactic, would be an ideal solution.

davidg
  • 5,868
  • 2
  • 33
  • 51
  • You might want to look into the code of `try0`. AFAIK, it does a similar trick to what you propose with your anti-quotation. – Lars Noschinski Mar 06 '13 at 07:09
  • @LarsNoschinski: `try0` initially looked promising, but unfortunately works using proof-state (`state`) objects, which still can't be used in a situation where a `tactic` is called for. A possible approach could be to inject a partially-proven `thm` into a new proof state object, apply a method to it, and then extract the result; unfortunately it seems that there is no obvious mechanism to do this either. – davidg Mar 07 '13 at 02:55
  • With several questions and answers and comments spread over them, it is getting a bit difficult to follow. Better start a plain-old mailing list discussion at `isabelle-users`. – Makarius Mar 07 '13 at 11:10

3 Answers3

6

I see a variety of possibilities, it depends a bit on the context of your the application what is best. Note that in general, individual ML code for proof automated used to be common-place in the very old times, but it is relatively rare today. For example, compare the amount of custom tactics in rather small HOL-Bali (started in 1997) with the large JinjaThreads in AFP (started in 2007 and continued until recently).

Nesting ML antiquotations like @{tactic} would in principle work, but you would quickly run into further questions, like what happens if your theorem arguments should be again Isar or ML source.

Instead of antiquoting tactic building blocks in ML, a more basic approach is to quote your proof precedure in Isar by giving it regular method syntax like this:

ML {*
  (*foo_tac -- the payload of what you want to do,
    note the dependency on ctxt: Proof.context*)
  fun foo_tac ctxt =
    let
      val my_ctxt =
        ctxt |> Simplifier.map_simpset
         (fold Splitter.add_split @{thms prod.splits} #>
          Simplifier.add_simp @{thm split_def})
    in ALLGOALS (clarsimp_tac my_ctxt) end
*}

method_setup foo = {*
  (*concrete syntax like "clarsimp", "auto" etc.*)
  Method.sections Clasimp.clasimp_modifiers >>
    (*Isar method boilerplate*)
    (fn _ => fn ctxt => SIMPLE_METHOD (CHANGED (foo_tac ctxt)))  
*}

Here I've first made a conventional foo_tac definition in Isabelle/ML, and then wrapped it up the usual Isar way as proof method. The latter means you have wrappers like SIMPLE_METHOD taking care of pushing "chained facts" into your goal state, and CHANGED to ensure that the Isar method makes progress (like simp or auto).

The foo_tac example assumes that your modification of the context (or its simpset) is constant, by the hard-wired split rules. If you want to have further parameters there, you can include that in the concrete method syntax. Note that Method.sections is already quite sophisticated in this respect. More basic argument parsers are given in the section "Defining proof methods" of the isar-ref manual. You should also look at existing examples by searching the sources for method_setup (in Isabelle/Isar) or Method.setup (in Isabelle/ML).

If you still want to do ML antiquotations instead of concrete method syntax, one could try a variant of @{context} that allows modifiers like this:

@{context simp add: ...}

That is a bit speculative, invented on the spot, and might turn out as bad practice. As I've said, fine-grained tactic programming in Isabelle became a bit out of use in recent years, although ML is an integral part of the Isabelle framework. If you pose a more concrete question with more of the application context, we can reconsider the antiquotation approach.

Makarius
  • 2,165
  • 18
  • 20
  • I am not wedded to using an anti-quotation (how to add arguments from ML variables is a good point), but rather am just looking for a convenient way to instantiate off-the-shelf Isabelle tactics (such as `clarsimp`, `auto`, etc) from within ML code. The context is the [AutoCorres](http://ssrg.nicta.com.au/projects/TS/autocorres/) project, which needs to generate many proofs based on user C code automatically. Writing/prototyping such automated proof methods can become quite tedious using the Isabelle ML interface that I have used in my question. – davidg Mar 05 '13 at 22:31
  • The example of `foo_tac` and method `foo` above were meant as such a "convenient instantiation" of clarsimp, auto. Instead of inlining large `tactic` ML expressions, you have a mix of auxiliary ML tactics and Isar method syntax. You can also look at src/HOL/Auth in the Isabelle sources as an example with many specific ML tactics and Isar methods in that sense (it is an updated version of a classic tactic-only application of Isabelle from the 1990-ies). – Makarius Mar 06 '13 at 12:37
  • `src/HOL/Auth` appears to be crafting methods to help solve theorems inside hand-written theory files. In my context, I am solving theorems that have been dynamically created based on an input C file---there is no proof script at the bottom, but rather I am dealing with `thm` objects generated by `Goal.init`, which require a `tactic` to process. When developing such tactics, I often just want to use "built-in" Isabelle tactics, but without the verbosity of generating the equivalent of a `foo_tac` body for every such use. – davidg Mar 07 '13 at 06:29
3

Additional to the other answers, I think it's worth mentioning that there is a new high-level tactic/proof method construction language (similar to Ltac in Coq) called Eisbach in Isabelle2015, which aims to be easier to understand and maintain.

Arets Paeglis
  • 3,856
  • 4
  • 35
  • 44
1

The Method class appear to provide enough of an interface to extract out a tactic, via a cases_tactic as follows:

(*
 * Generate an ML tactic object of the given Isar string.
 *
 * For example,
 *
 *   mk_tac "auto simp: field_simps intro!: ext" @{context}
 *
 * will generate the corresponding "tactic" object.
 *)
fun mk_tac str ctxt =
let
  val parsed_str = Outer_Syntax.scan Position.start str
      |> filter Token.is_proper
      |> Args.name
  val meth = Method.method (Proof_Context.theory_of ctxt)
      (Args.src (parsed_str, Position.start)) ctxt
in
  Method.apply (K meth) ctxt [] #> Seq.map snd
end

or alternatively as an anti-quotation:

(*
 * Setup an antiquotation of the form:
 *
 *    @{tactic "auto simp: foo intro!: bar"}
 *
 * which returns an object of type "context -> tactic".
 *
 * While this doesn't provide any benefits over a direct call to "mk_tac" just
 * yet, in the future it may generate code to avoid parsing the tactic at
 * run-time.
 *)
val tactic_antiquotation_setup =
let
  val parse_string =
    ((Args.context -- Scan.lift Args.name) >> snd)
      #>> ML_Syntax.print_string
      #>> (fn s => "mk_tac " ^ s)
      #>> ML_Syntax.atomic
in
  ML_Antiquote.inline @{binding "tactic"} parse_string
end

and setup in a theory file as follows:

setup {*
  tactic_antiquotation_setup
*}

which can then be used as follows:

lemma "(a :: nat) * (b + 1) = (a * b) + a"
  by (tactic {* @{tactic "metis Suc_eq_plus1 mult_Suc_right nat_add_commute"} @{context} *})

as desired.

davidg
  • 5,868
  • 2
  • 33
  • 51