I was studying ways to trasnlate:
apply(rewrite in "_ / ⌑" some_theorem)
into ML, and I came up with the following:
apply(tactic ‹
let
val pat = [
Rewrite.In,
Rewrite.Term (#{const divide(real)} $ Var (("c", 0), \<^typ>‹real›) $
Rewrite.mk_hole 1 (\<^typ>‹real›), []),
Rewrite.In
]
val to = NONE
in
CCONVERSION (Rewrite.rewrite_conv #{context} (pat, to) #{thms juanito_def}) 1
end
›)
here I am saying "please pattern match with any subterm of the term in the quotient". You may see previous edits of the question to see interesting patterns.
Some interesting conclusions of having used the library are
at : allows to select a subterm in which to pattern match
in : allows to specify all the subterms of a term
terms will be pattern-matched with the resulting patterns.
Not understood
Could you give an explanation of what the following tactic is doing?
lemma
assumes "Q (λb :: int. P (λa. a + b) (λa. a + b))"
shows "Q (λb :: int. P (λa. a + b) (λa. b + a))"
apply (tactic ‹
let
val (x, ctxt) = yield_singleton Variable.add_fixes "x" \<^context>
val pat = [
Rewrite.Concl,
Rewrite.In,
Rewrite.Term (Free ("Q", (\<^typ>‹int› --> TVar (("'b",0), [])) --> \<^typ>‹bool›)
$ Abs ("x", \<^typ>‹int›, Rewrite.mk_hole 1 (\<^typ>‹int› --> TVar (("'b",0), [])) $ Bound 0), [(x, \<^typ>‹int›)]),
Rewrite.In,
Rewrite.Term (#{const plus(int)} $ Free (x, \<^typ>‹int›) $ Var (("c", 0), \<^typ>‹int›), [])
]
val to = NONE
in CCONVERSION (Rewrite.rewrite_conv ctxt (pat, to) #{thms add.commute}) 1 end
›)
apply (fact assms)
done
The first Rewrite.Term and the fixing of a variable are a bit obscure to me.
Related
In my Isabelle/HOL theories, I like using unnamed lemmas as examples.
Here is an example how some of my theories look like.
definition foo_function :: "nat ⇒ nat" where "foo_function x = x+1"
text‹Example:›
lemma "foo_function 3 = 4" by eval
I feel the whole theory would read much nicer if I have an example keyword, which is basically equivalent to an unnamed lemma. Here is what I would like to write:
definition foo_function :: "nat ⇒ nat" where "foo_function x = x+1"
example "foo_function 3 = 4" by eval
Is there a super simple and stable way to set this up?
Control-click on lemma (besides proofs, this is the most important feature in Isabelle. Really. You can control-click on nearly everything to understand how it is defined) and copy-pasting the setup:
theory Scratch
imports Main
keywords "example" :: thy_goal_stmt
begin
ML ‹
local
val long_keyword =
Parse_Spec.includes >> K "" ||
Parse_Spec.long_statement_keyword;
val long_statement =
Scan.optional (Parse_Spec.opt_thm_name ":" --| Scan.ahead long_keyword) Binding.empty_atts --
Scan.optional Parse_Spec.includes [] -- Parse_Spec.long_statement
>> (fn ((binding, includes), (elems, concl)) => (true, binding, includes, elems, concl));
val short_statement =
Parse_Spec.statement -- Parse_Spec.if_statement -- Parse.for_fixes
>> (fn ((shows, assumes), fixes) =>
(false, Binding.empty_atts, [], [Element.Fixes fixes, Element.Assumes assumes],
Element.Shows shows));
fun theorem spec schematic descr =
Outer_Syntax.local_theory_to_proof' spec ("state " ^ descr)
((long_statement || short_statement) >> (fn (long, binding, includes, elems, concl) =>
((if schematic then Specification.schematic_theorem_cmd else Specification.theorem_cmd)
long Thm.theoremK NONE (K I) binding includes elems concl)));
val _ = theorem \<^command_keyword>‹example› false "example";
in end
›
example True
by eval
end
I'm confused about the all introduction meta rule in Isabelle. The papers say it should be:
From P deduce ⋀ x. P whenever x is not a free variables in the asumptions.
This is confusing to me. I understand better wikipedia's one:
From (P y) deduce ⋀ x. P x whenever y is not free in the (implicit) assumptions and x is not free in P.
How is the meta-forall rule encoded in Isabelle? Here is the source code:
(*Forall introduction. The Free or Var x must not be free in the hypotheses.
[x]
:
A
------
⋀x. A
*)
fun forall_intr
(ct as Cterm {maxidx = maxidx1, t = x, T, sorts, ...})
(th as Thm (der, {maxidx = maxidx2, shyps, hyps, tpairs, prop, ...})) =
let
fun result a =
Thm (deriv_rule1 (Proofterm.forall_intr_proof x a) der,
{cert = join_certificate1 (ct, th),
tags = [],
maxidx = Int.max (maxidx1, maxidx2),
shyps = Sorts.union sorts shyps,
hyps = hyps,
tpairs = tpairs,
prop = Logic.all_const T $ Abs (a, T, abstract_over (x, prop))});
fun check_occs a x ts =
if exists (fn t => Logic.occs (x, t)) ts then
raise THM ("forall_intr: variable " ^ quote a ^ " free in assumptions", 0, [th])
else ();
in
(case x of
Free (a, _) => (check_occs a x hyps; check_occs a x (terms_of_tpairs tpairs); result a)
| Var ((a, _), _) => (check_occs a x (terms_of_tpairs tpairs); result a)
| _ => raise THM ("forall_intr: not a variable", 0, [th]))
end;
Suppose I am a mathematician with only some notions of programming. How would you convince me the piece of code below implements the meta-forall rule in a sensible manner?.
I'm trying to do:
datatype my_bool = true | false
value "true" (* it has value true with type my_bool *)
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool" where
"conj true true = true" |
"conj _ _ = false"
lemma "conj true true = true"
apply (simp only: conj_def)
but I get error:
Undefined fact: "conj_def"⌂
I understand the error but not why I can't apply a single simp like I do with definitions. Is this possible with functions at all?
When you define a new constant using the command definition, a theorem like conj_def is provided automatically (actually, it is possible to control the name of this theorem). The command fun does not provide a theorem name_def automatically (where name is the name of the constant). However, it provides a variety of other theorems. You can see such theorems by typing print_theorems after the specification of a constant using the command fun. For example,
datatype my_bool = true | false
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool"
where
"conj true true = true"
| "conj _ _ = false"
print_theorems
For example, in the code listing above the command fun provides the fact conj.simps, which is, most likely, what you were looking for:
lemma "conj true true = true"
by (simp only: conj.simps)
Technically, it is possible to recover the original definitional axioms in Isabelle/ML for any constant, including conj (some insight about the definitional principles can be gained from [1], but there could exist more specialized references for this):
theory Scratch
imports Main
keywords "get_da" :: diag
begin
ML‹
(*the implementation of axioms_of_ci and da_of_ci are based on elements of
the code HOL/Types_To_Sets/unoverloading.ML*)
local
fun match_args (Ts, Us) =
if Type.could_matches (Ts, Us)
then
Option.map Envir.subst_type
(
SOME (Type.raw_matches (Ts, Us) Vartab.empty)
handle Type.TYPE_MATCH => NONE
)
else NONE;
in
fun axioms_of_ci thy defs (c, T) =
let
val const_entry = Theory.const_dep thy (c, T);
val Uss = Defs.specifications_of defs (fst const_entry);
in
Uss
|> filter (fn spec => is_some (match_args (#lhs spec, snd const_entry)))
|> map (fn Us => (#def Us, #description Us))
end;
fun das_of_ci thy defs = axioms_of_ci thy defs
#> map #1
#> filter is_some
#> map (the #> try (Thm.axiom thy))
#> filter is_some
#> map (the #> Drule.abs_def);
end;
fun apdupr f x = (x, f x);
fun axioms_of_const ctxt (c, T) =
let
val thy = Proof_Context.theory_of ctxt
val defs = Theory.defs_of thy
in das_of_ci thy defs (c, T) end;
fun process_da t st =
let
val ctxt = Toplevel.context_of st
val const = t
|> Proof_Context.read_term_pattern ctxt
|> dest_Const
val _ = const
|> axioms_of_const ctxt
|> map (Thm.string_of_thm ctxt)
|> map writeln
in () end;
val tts_find_sbts = Outer_Syntax.command
\<^command_keyword>‹get_da›
"print definitional axioms"
(Parse.const >> (process_da #> Toplevel.keep));
›
datatype my_bool = true | false
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool"
where
"conj true true = true"
| "conj _ _ = false"
print_theorems
lemma "conj true true = true"
by (simp only: conj.simps)
get_da conj_graph
get_da conj_sumC
get_da conj
text‹The type of the input to the command #{command get_da} is important:›
get_da ‹plus::nat⇒nat⇒nat›
get_da ‹plus::int⇒int⇒int›
end
However, as noted by Manuel Eberl in the comments, such axioms are not particularly useful for most practical purposes for the end users.
Isabelle version: Isabelle2020
References:
Haftmann F, Wenzel M. Local Theory Specifications in Isabelle/Isar. In: Berardi S, Damiani F, de’Liguoro U, editors. Types for Proofs and Programs. Heidelberg: Springer; 2009. p. 153–68.
The third chapter of CPDT briefly discusses why negative inductive types are forbidden in Coq. If we had
Inductive term : Set :=
| App : term -> term -> term
| Abs : (term -> term) -> term.
then we could easily define a function
Definition uhoh (t : term) : term :=
match t with
| Abs f => f t
| _ => t
end.
so that the term uhoh (Abs uhoh) would be non-terminating, with which "we would be able to prove every theorem".
I understand the non-termination part, but I don't get how we can prove anything with it. How would one prove False using term as defined above?
Reading your question made me realize that I didn't quite understand Adam's argument either. But inconsistency in this case results quite easily from Cantor's usual diagonal argument (a never-ending source of paradoxes and puzzles in logic). Consider the following assumptions:
Section Diag.
Variable T : Type.
Variable test : T -> bool.
Variables x y : T.
Hypothesis xT : test x = true.
Hypothesis yF : test y = false.
Variable g : (T -> T) -> T.
Variable g_inv : T -> (T -> T).
Hypothesis gK : forall f, g_inv (g f) = f.
Definition kaboom (t : T) : T :=
if test (g_inv t t) then y else x.
Lemma kaboom1 : forall t, kaboom t <> g_inv t t.
Proof.
intros t H.
unfold kaboom in H.
destruct (test (g_inv t t)) eqn:E; congruence.
Qed.
Lemma kaboom2 : False.
Proof.
assert (H := #kaboom1 (g kaboom)).
rewrite -> gK in H.
congruence.
Qed.
End Diag.
This is a generic development that could be instantiated with the term type defined in CPDT: T would be term, x and y would be two elements of term that we can test discriminate between (e.g. App (Abs id) (Abs id) and Abs id). The key point is the last assumption: we assume that we have an invertible function g : (T -> T) -> T which, in your example, would be Abs. Using that function, we play the usual diagonalization trick: we define a function kaboom that is by construction different from every function T -> T, including itself. The contradiction results from that.
I investigated Coq a little, with its dependent types. I have only the foggiest idea about it all, but now I have in mind that I want a bit vector as a bool list, in which the length of the vector is part of the type.
(This question is the possible predecessor of another question. In the next question, if I ask it, I'll ask whether I can recover what I lose, when I use typedef as below.)
(For this question, the question is at the bottom)
Here are the requirements for the type I want:
It has to use bool list, so that I can directly or indirectly do pattern matching and recursion on the list, and
the length of the vector has to be specified in the type.
Here is what I have:
typedef bitvec_4 = "{bl::bool list. length bl = 4}"
by(auto, metis Ex_list_of_length)
It's important that the length of the list be part of the type, because I want to use the type with a definition, where all lists are known to be of the same size, like with this simple example:
definition two_bv4_to_bv4 :: "bitvec_4 => bitvec_4 => bitvec_4" where
"two_bv4_to_bv4 x y = x"
What I don't want, in a theorem, is to have to specify the length of the lists. Type classes would eventually come into play somehow, but I want, as I say, the length to be specified in the type definition.
Definition and type signatures. Where do I let n = 4!!? (a tech joke of minimal humor-value)
Now, I try to generalize with a typedef like this, in which the length is a variable:
typedef bitvec_n = "{(bl::bool list, n::nat). length bl = n}"
by(auto)
That's no good. In a definition like this next one, my type doesn't guarantee that all lists are of the same length:
definition two_bvn_to_bvn :: "bitvec_n => bitvec_n => bitvec_n" where
"two_bvn_to_bvn x y = x"
The question? (I think so)
I've experimented a little with types like bitvec_4 above. If I don't run into big roadblocks, I might try to make big use of them.
I could define types like the above for powers of 2, up to, say, 1024 bits, along with type classes that reflect their common properties.
But, is there a better way to do this? It has to be somewhat straightforward, I think, with the use of bool list.
Update (got the answer for what it was actually about)
Based on Manuel's answer, I include here a self-contained theory.
It's mostly a duplication of Manuel's source, but at the end, my functions swap_bl and swap_2bv, along with the final use of value, show the end result of what I was trying to accomplish. My comments emphasize the problems that were on my mind, and possibly, my end application shows why I haven't looked to HOL/Word as a solution.
For a typedef type, to do pattern matching indirectly, similar to that with swap_bl and 2 bitvec, I was using the Abs and Rep functions together as inverses. One problem, as Manuel pointed out, is that I can feed an Abs function a bool list of the wrong length, and it won't give an error. Another big problem is the abstraction violations due to the use of the Abs function.
Those problems, and wanting to know if I could recover the use of value for my typedef type, would have been parts of my next question, but all that's been answered here.
theory i141210ac__testing_out_manuels_answer
imports Main "~~/src/HOL/Library/Numeral_Type"
begin
(*Basic type definition.*)
typedef ('n::finite) bitvec = "{bs :: bool list. length bs = CARD('n)}"
morphisms bitvec_to_list Abs_bitvec
by (simp add: Ex_list_of_length)
setup_lifting type_definition_bitvec
lift_definition nth :: "('n::finite) bitvec => nat => bool" (infixl "$" 90)
is List.nth .
(*Can't use 'value' yet for 'nth', or I get an abstraction violation.*)
term "(Abs_bitvec [True,False] :: 2 bitvec) $ 1"
(*Truncate or fill the list: needed to set things up for 'value'.*)
definition set_length :: "nat => bool list => bool list" where
"set_length n xs = (if length xs < n
then xs # replicate (n - length xs) False
else take n xs)"
lemma length_set_length [simp]: "length (set_length n xs) = n"
unfolding set_length_def by auto
definition list_to_bitvec :: "bool list => ('n::finite) bitvec" where
"list_to_bitvec xs = Abs_bitvec (set_length CARD('n) xs)"
(*Finishing the magic needed for 'value'.*)
lemma list_to_bitvec_code [code abstract]:
"bitvec_to_list (list_to_bitvec xs :: ('n::finite) bitvec)
= set_length CARD('n) xs"
unfolding list_to_bitvec_def by(simp add: Abs_bitvec_inverse)
(*Inverses for lists of length 2: no abstraction violations.*)
value "list_to_bitvec (bitvec_to_list x) :: 2 bitvec"
value "bitvec_to_list (list_to_bitvec x :: 2 bitvec)"
(*The magic now kicks in for 'value' and 'nth'. Understanding is optional.*)
value "(list_to_bitvec [True,False] :: 2 bitvec) $ 1" (*OUTPUT: False.*)
(*For my use, the primary context of all this is pattern matching on lists.
I can't pattern match on a 'typedef' type directly with 'fun', because
it's not a 'datatype'. I do it indirectly.*)
fun swap_bl :: "bool list => bool list" where
"swap_bl [a,b] = [b,a]"
|"swap_bl _ = undefined"
definition swap_2bv :: "2 bitvec => 2 bitvec" where
"swap_2bv bv = list_to_bitvec (swap_bl (bitvec_to_list bv))"
value "swap_2bv (list_to_bitvec [a,b] :: 2 bitvec)" (*
OUTPUT: "Abs_bitvec [b, a]" :: "2 bitvec" *)
(*Whether that's all a good idea, that's about the future, but it appears the
hard work, recovering the use of 'value', along with generalizing length,
has been done by Manuel, and the authors of Numeral_Type and its imports.*)
end
Isabelle does not support dependent types, but there are ways to still do what you want to do. For instance, there is already a stack of type classes and type syntax for type-level natural numbers.
theory Scratch
imports Main "~~/src/HOL/Library/Numeral_Type"
begin
lemma "(UNIV :: 4 set) = {0,1,2,3}"
by (subst UNIV_enum) eval
As you can see, the type 4 is a type that contains the numbers from 0 to 3. Incidentally, this can also be used for computations in modular arithmetic:
lemma "((2 + 3) :: 4) = 1" by simp
lemma "((2 * 3) :: 4) = 2" by simp
You can use these numeral types to parametrise your bit vectors with a length:
typedef ('n::finite) bitvec = "{bs :: bool list. length bs = CARD('n)}"
morphisms bitvec_to_list Abs_bitvec
by (simp add: Ex_list_of_length)
setup_lifting type_definition_bitvec
You can access the n-th element of a bit vector by lifting the nth function from Boolean lists to bit vectors, which works automatically:
lift_definition nth :: "('n::finite) bitvec ⇒ nat ⇒ bool" (infixl "$" 90) is List.nth .
Converting boolean lists to bit vectors is a bit tricky, because the list you get in might not have the correct length; the expression list_to_bitvec [True] :: 2 bitvec would typecheck, but is obviously problematic. You could solve this either by returning undefined or, perhaps more appropriate in this instance, filling up the list with False or truncating it to get the right length:
definition set_length :: "nat ⇒ bool list ⇒ bool list" where
"set_length n xs = (if length xs < n then xs # replicate (n - length xs) False else take n xs)"
lemma length_set_length[simp]: "length (set_length n xs) = n"
unfolding set_length_def by auto
Now we can define a function that converts a list of Booleans to a bit vector:
definition list_to_bitvec :: "bool list ⇒ ('n::finite) bitvec" where
"list_to_bitvec xs = Abs_bitvec (set_length CARD('n) xs)"
However, we are not allowed to use Abs_bitvec in code equations; if you tried to evaluate, say, list_to_bitvec [True] :: 1 bitvec, you would get an abstraction violation. We have to give an explicit code abstract equation in terms of the morphism list_to_bitvec:
lemma list_to_bitvec_code[code abstract]:
"bitvec_to_list (list_to_bitvec xs :: ('n::finite) bitvec) = set_length CARD('n) xs"
unfolding list_to_bitvec_def by (simp add: Abs_bitvec_inverse)
And now we are basically done and can do e.g. this:
definition myvec :: "4 bitvec" where "myvec = list_to_bitvec [True, False, True]"
value myvec
(* Output: "Abs_bitvec [True, False, True, False]" :: "4 bitvec" *)
value "myvec $ 2"
(* Output: "True" :: "bool" *)
Note that you always have to annotate the result of list_to_bitvec with its length; Isabelle can not infer the length.
You may also want to have a look at the Word theory in ~~/src/HOL/Word/; it implements machine words of fixed length with all kinds of bit operations like NOT, AND, OR, etc.:
value "42 AND 23 :: 32 word"
(* Output: "2" :: "32 word" *)
value "293 :: 8 word"
(* Output: "37" :: "8 word" *)
value "test_bit (42 :: 8 word) 1"
(* Output: "True" :: "bool" *)
value "set_bit (42 :: 8 word) 2 True"
(* Output: "46" :: "8 word" *)
value "(BITS i. i < 4) :: 8 word"
(* Output: "15" :: "8 word" *)
Another related type are the vectors in src/HOL/Library/Multivariate_Analysis/Finite_Cartesian_Product.