From tfm%computer-lab.cambridge.ac.uk@NSFnet-Relay.AC.UK  Tue Jan 16 10:40:01 1990
Received: by iris.ucdavis.edu (5.57/UCD.EECS.2.0)
        id AA11364; Tue, 16 Jan 90 10:40:01 PST
Received: from ucdavis.ucdavis.edu by clover.ucdavis.edu (5.59/UCD.EECS.1.11)
        id AA05483; Tue, 16 Jan 90 10:44:55 PST
Received: by ucdavis.ucdavis.edu (5.51/UCD1.41)
        id AA09373; Tue, 16 Jan 90 10:32:17 PST
Received: from sun.nsfnet-relay.ac.uk by vax.NSFnet-Relay.AC.UK
           via Janet with NIFTP  id aa20665; 16 Jan 90 17:54 GMT
Received: from moorhen.cl.cam.ac.uk by gnnt.Cl.Cam.AC.UK id aa17634;
          16 Jan 90 18:00 GMT
Received: by uk.ac.cam.cl.moorhen (4.0/SMI-3.0DEV3)
        id AA07473; Tue, 16 Jan 90 18:00:01 GMT
Date: Tue, 16 Jan 90 18:00:01 GMT
From: tfm%computer-lab.cambridge.ac.uk@NSFnet-Relay.AC.UK
Message-Id: <9001161800.AA07473@uk.ac.cam.cl.moorhen>
To: info-hol%clover.ucdavis.edu@NSFnet-Relay.AC.UK
Subject: using FIRST_ASSUM in your tactic proofs.

The following is really a reply to a question from Phil Windley, but it
may be of interest to other users, so I'm posting it to info-hol.

FIRST_ASSUM
-----------

The problem has to do with the function FIRST_ASSUM.  As stated by the
documentation, FIRST_ASSUM maps a tactic-producing function down the
assumptions of a goal, and then applies the first tactic in the resulting
list for which application succeeds.  I.e.

     FIRST_ASSUM f ([a1;a2;...;an], g)

is equivalent to

     (f (ASSUME a1)) ORELSE (f (ASSUME a2)) ... ORELSE (f (ASSUME an))

applied to the goal ([a1;a2;...;an], g).  This function is very useful
for making use of a particular assumption on the assumption list of a goal,
without mentioning either the assumption itself or it's position on the
assumption list.  That is, FIRST_ASSUM is very useful for writing tactics
which do not depend on the ordering of assumptions.

EXAMPLES
--------

Here are some simple examples of FIRST_ASSUM, tactics which one can
use almost every day.

1) FIRST_ASSUM ACCEPT_TAC

   This looks through the assumptions for one which exactly
   matches the goal to be proved.  Very useful when the "answer"
   is just "there"... on the assumption list.  And much faster
   than the overkill solution: ASM_REWRITE_TAC [].

   A variant on FIRST_ASSUM ACCEPT_TAC is:

2) FIRST_ASSUM MATCH_ACCEPT_TAC

   Use when some specialization of an assumption solves the goal.

3) FIRST_ASSUM MATCH_MP_TAC

   This is one of my all-time favourites.  Suppose the conclusion of
   your goal is something like "P[x]", and suppose that you have
   an assumption "!y. Q[y] ==> P[y]" on the assumption list.  Clearly,
   the goal can be reduced to "Q[x]", and that's exactly what FIRST_ASSUM
   MATCH_MP_TAC does.


THE PROBLEM
-----------

Phil was trying to use FIRST_ASSUM to do something with a particular
specialization (SPEC) of some assumption.  Suppose you have an assumption

    !x. P x \/ Q x

and you want to generate a case split on whether P n or Q n, for some
particular n.   Futhermore suppose that the assumption above occurs
somewhere among a lot of assumptions which are NOT universally quantified,
for example as in:

    set_goal (["T"; "!x:num. P x \/ Q x"; "F"], "G:bool");;

Now, one might hope to generate the case split "P n or Q n" by using

    FIRST_ASSUM (STRIP_ASSUME_TAC o SPEC "n:num")

to find the first assumption which can be specialised to "n", and then
break it up into the two disjuncts.  This tactic, however, fails.  The
problem is that "SPEC" fails when applied to the first assumption "T"
on the assumption list of the goal.  The reason that FIRST_ASSUM fails
is that the tactic

    FIRST_ASSUM (fun:thm -> tactic)

first applies the function fun to the assumptions to get a list of
tactics, and THEN applies the first TACTIC which succeeds on the goal.
FIRST_ASSUM does not catch failures in applying the function fun to
the assumptions.

To get around this problem, one could try:

    FIRST_ASSUM (\th. STRIP_ASSUME_TAC(SPEC "n:num" th) ? NO_TAC)

This will now work, since the "?" catches any failures of SPEC and
yields NO_TAC, which can itself fail when applied to the goal.

An alternative solution is to redefine FIRST_ASSUM (actually, to define
a new version of FIRST_ASSUM) which does not fail if the supplied function
fails on the ASSUME of any assumption.  Here is the built-in definition
of FIRST_ASSUM:

   let MAP_FIRST tacf lst =  FIRST (map tacf lst);;
   let FIRST_ASSUM = ASSUM_LIST o MAP_FIRST;;

All we have to do is to define:

   let MAPFILTER_FIRST tacf lst =  FIRST (mapfilter tacf lst);;

and then define a new version of FIRST_ASSUM by:

   let NEW_FIRST_ASSUM = ASSUM_LIST o MAPFILTER_FIRST;;

The only difference here is that we've used mapfilter instead of map.
The tactic:

   NEW_FIRST_ASSUM (STRIP_ASSUME_TAC o SPEC "n:num")

now does what we want and won't fail when it hits an assumption that
can't be SPECed (try it).

Actually, one really wouldn't want to define a new tactic with a silly
name like NEW_FIRST_ASSUM. Normally, you would just put the tactic:

   ASSUM_LIST (FIRST o (mapfilter (STRIP_ASSUME_TAC o SPEC "n:num")))

in-line, without giving it a special name.


I hope people find this helpful.

Tom

