Isabelle: Too many precedences in mixfix annotation - isabelle

I am experimenting the following example classes.pdf in Isabelle tutorial (2020), pp6.
I did my best translating the PDF content into proof text, such as changing α in the PDF to 'a, adding quotes etc.
class group = monoidl +
fixes inverse :: "'a ⇒ 'a" ("(-÷)" [1000] 999)
assumes invl : "x ÷ ⊗ x = 𝟭"
However, the definition above still doesn't parse, with an error:
Too many precedences in mixfix annotation
I am not sure how the inverse notation should be declared. I've encountered infix notation only so far.
Can someone help explain how the fix the "mixfix" annotation in the example?

According to the Isabelle sources of the document, it is an underscore, not a hyphen:
class group = monoidl +
fixes inverse :: "\<alpha> \<Rightarrow> \<alpha>" ("(_\<div>)" [1000] 999)
assumes invl: "x\<div> \<otimes> x = \<one>"
Replacing underscores by hyphens is a feature of the document generation. Hyphen look nicer than underscore (at least in patterns).

Related

Is there a way to get a complete list of all kinds of operators/constructors of Isabelle?

For example, Isabelle can represent "and" as "^", "or" as "v".....Is there a way to get a complete list of all of these kinds of operators/constructors?
There is the print_syntax command, but its output might look a bit intimidating. But, for instance, the following line
logic(55) = logic(55) "∘" logic(56) ⇒ "\<^const>Fun.comp
tells you that the symbol ∘ is an infix operator with precedence 55 that maps to the constant Fun.comp. The corresponding declaration is this:
definition comp :: "('b ⇒ 'c) ⇒ ('a ⇒ 'b) ⇒ 'a ⇒ 'c" (infixl "∘" 55)
where "f ∘ g = (λx. f (g x))"
The more usual way to discover these notations is to either
try the obvious notation (for many things, it is just what one would expect, as is the case for the function composition above).
Find out what the constant is called and then look around the place where it is defined to see what notation is set up for it.
Not that I know of.
A good starting point is the list of all symbols in Main. However, that does not contain all symbols and does not provide a mapping from symbols to definitions.
In general, it is not that useful to find how things are abbreviated:
If you have the symbol, you can click on it to find the definition.
If you have the symbol and don't know how to type it, you can go the symbol panel of Isabelle/jEdit to see how to type it.
If you have the definition, just type term "and a b" and the output shows you the version with the symbol.
So the real question is how to find the right definition, not how to find the right symbol. But that is a lot harder.

Simple Isabelle/HOL theory: Inner lexical error⌂ Failed to parse prop

I am trying to create my first Isabelle/HOL theory that encodes Monoidal logic but I am stuck already with the first axiom - identity axiom. I do not understand what is wrong with my code, because error message does not give explanation. My theory is:
theory MonoidalLogic
imports "~~/src/ZF/Main" Sequents
begin
consts
Mult :: "[o, o]⇒o"
axiomatization where
identity: "P ⊢ P"
(* and
cut: "φ⊢ψ;ψ⊢ρ⟹φ⊢ρ" and
l: "φ⊢⊤⨂ψ⟺φ⊢ψ" and
r: "φ⊢ψ⨂⊤⟺φ⊢ψ"*)
end
Error message is at identity: "P ⊢ P":
Inner lexical error⌂
Failed to parse prop
What should I correct to move forward?
Note added: I am trying to implement Monoidal logic in similar manner how the Linear logic is implemented in Isabelle/HOL: https://www.cl.cam.ac.uk/research/hvg/Isabelle/dist/library/Sequents/Sequents/ILL.html And identity: "P ⊢ P" is the first axiom for many logics, including Linear logic and Monoidal logic. I am starting to feel that ⊢ is not the first class character for Isabelle but it may be introduced by syntax rule
syntax "_Trueprop" :: "single_seqe" ("((_)/ ⊢ (_))" [6,6] 5)
in Linear logic. I don' understand this rule but I guess I should introduce something like this in my theory as well...

What does `class` do in Isabelle

I'm trying to understand what
class gcd = zero + one + dvd +
fixes gcd :: "'a ⇒ 'a ⇒ 'a"
and lcm :: "'a ⇒ 'a ⇒ 'a"
begin
from GCD.thy means. Browsing through Nipkow's Programming and Proving didn't reveal any example, that explains what class does, not did the Tutorial on Isabelle/HOL.
Can you please explain to me what exactly class does ? I assume it defines a datatype.
Also, that fact that nothing follows after the last + seems irritating.
This is not a datatype, but a Haskell-style type class. Isabelle's type class system is described in Florian Haftmann's manual. The concept of locales is also closely related. Both are, I would say, advanced concepts in Isabelle that I would not recommend for a beginner to delve into. (which is also why the tutorials don't mention them)
To give you a brief overview: A type class is a way to talk about a collection of types that have something in common; for instance, the ring class talks about all types that form a ring: they have a 0 element, an addition and multiplication operation, and all of these fulfil certain laws. The gcd class you found is a type class for types that have a GCD and LCM defined on them (although it does not yet demand that these fulfil any laws; this is done in the semiring_gcd class.)
There is something after the final +: the fixes. The class declaration means that the class gcd is the intersection of the classes zero (type contains an element 0), the class one (contains a 1) and the class dvd (contains a notion of divisibility), and it additionally requires the presence of two functions gcd and lcm.
This class is a bit odd because it is purely syntactic, i.e. it does not require any laws to hold. Let's therefore also look at semiring_gcd:
class semiring_gcd = normalization_semidom + gcd +
assumes gcd_dvd1 [iff]: "gcd a b dvd a"
and gcd_dvd2 [iff]: "gcd a b dvd b"
and gcd_greatest: "c dvd a ⟹ c dvd b ⟹ c dvd gcd a b"
and normalize_gcd [simp]: "normalize (gcd a b) = gcd a b"
and lcm_gcd: "lcm a b = normalize (a * b) div gcd a b"
This class requires the presence of a gcd/lcm (by being based on the gcd class) and the type has to be a normalization_semidom (see below). Then there are a number of assumptions that also need to be satisfied.
So, to summarise, type classes are a nice way of organising common properties and operation of types, but I'd say that for beginners, it's not really important to understand every detail of them in order to be able to use them.
Bonus content: (not terribly relevant, just for the curious)
If you're wondering about the normalization_semidom and normalize: This is a very technical thing. Basically, if you have several associated elements (like 1 and -1, or, with polynomials ℝ[X], X and 2X), you typically have some notion of which of these is the ‘canonical’ representative: if I asked you what the GCD of 2 and 3 is, you would probably say 1 and not -1, and if I asked what the GCD of 2X and X² ∈ ℝ[X] is, you would probably say X and not 2X.

What is a Quotient type pattern in Isabelle?

What is a "Quotient type pattern" in Isabelle?
I couldn't find any explanation over the internet.
It would be better if you would quote a little from where you saw the phrase. I know of "pattern matching," and I know of "quotient type," but I don't know of "quotient type pattern."
I prefer not to ask for clarification, and then wait, so I pick two of the three words, "quotient type." If I'm on the wrong track, it's still a worthy subject, and a big and important part of Isabelle/HOL.
There is the quotient_type keyword, and it allows you to define a new type with an equivalence relation.
It is part of the quotient package, described starting on page 248 of isar-ref.pdf. There happens to be a Wiki page, Quotient_type.
A more involved description is given by Brian Hufmann and Ondřej Kunčar. Go to Kunčar's web page and look at the two PDFs titled Lifting and Transfer: A Modular Design for Quotients in Isabelle/HOL, which are not exactly the same.
It happens to be that lifting and quotient types are heavily related, and not easy to understand, which is why I try to study a little here and there, like right now, to get a better understanding of it all.
Integers and Rationals in HOL Are Quotient Types, I Pick One as an Example, Integers
You can start by looking Int.thy.
For a quotient type, you need an equivalence relation, which defines a set, and intrel is what is used to define that set for type int.
definition intrel :: "(nat * nat) => (nat * nat) => bool" where
"intrel = (%(x, y) (u, v). x + v = u + y)"
This is the classic definition of the integers, based on the natural numbers. Integers are ordered pairs of natural numbers (and sets as I describe below), and they're equal by that definition.
For example, informally, (2,3) = (4,5) because 2 + 5 = 4 + 3.
I'm boring you, and you're waiting for the good stuff. Here's part of it, the use of quotient_type:
quotient_type int = "nat * nat" / "intrel"
morphisms Rep_Integ Abs_Integ
Those two morphisms come into play, if you want to strain your brain, and really understand what's going on, which I do. There are lots of functions and simp rules that quotient_type generates, and you have to do a lot of work to find it all, such as with the find_theorems command.
An Abs function abstracts an ordered pair to an int. Check these out:
lemma "Abs_Integ(1,0) = (1::int)"
by(metis one_int_def)
lemma "Abs_Integ(x,0) + Abs_Integ(y,0) ≥ (0::int)"
by(smt int_def)
They show that an int really is an ordered pair, under the hood of the engine.
Now I show the explicit types of those morphisms, along with Abs_int and Rep_int, which show int not only as an ordered pair, but as a set of ordered pairs.
term "Abs_int :: (nat * nat) set => int"
term "Abs_Integ :: (nat * nat) => int"
term "Rep_int :: int => (nat * nat) set"
term "Rep_Integ :: int => (nat * nat)"
I'm boring you again, but I have an emotional need to show some more examples. Two positive integers are equal if the components of the ordered pairs differ by one, such as these:
lemma "Abs_Integ(1,0) = Abs_Integ(3,2)"
by(smt nat.abs_eq split_conv)
lemma "Abs_Integ(4,3) = Abs_Integ(3,2)"
by(smt nat.abs_eq split_conv)
What would you expect if you added Abs_Integ(4,3) and Abs_Integ(3,2)? This:
lemma "Abs_Integ(2,3) + Abs_Integ(3,4) = Abs_Integ(2 + 3, 3 + 4)"
by(metis plus_int.abs_eq plus_int_def split_conv)
That plus_int in the proof is defined in Int.thy, on line 44.
lift_definition plus_int :: "int => int => int"
is "%(x, y) (u, v). (x + u, y + v)"
What is this lifting all about? That would put me at "days into" this explanation, and I'm only just starting to understand it a little.
The find_theorems shows there's lots of stuff hidden, as I said:
thm "plus_int.abs_eq"
find_theorems name: "Int.plus_int*"
More examples, but these are to emphasize that, under the hood of the engine, an int ties back into an equivalence class as a set, where I'm using intrel above to define the sets right:
term "Abs_int::(nat * nat) set => int"
term "Abs_int {(x,y). x + 3 = 2 + y}" (*(2,3)*)
term "Abs_int {(x,y). x + 4 = 3 + y}" (*(3,4)*)
lemma "Abs_int {(x,y). x + 3 = 2 + y} = Abs_int {(x,y). x + 100 = 99 + y}"
by(auto)
That auto proof was easy, but there's no magic coming through for me on this next one, even though it's simple.
lemma "Abs_int {(x,y). x + 3 = 2 + y} + Abs_int {(x,y). x + 4 = 3 + y}
= Abs_int {(x,y). x + 7 = 5 + y}"
apply(auto simp add: plus_int.abs_eq plus_int_def intrel_def)
oops
It could be that all I need to do is tap into something that's not a simp rule by default.
If quotient_type is not the "quotient type pattern" you're talking about, at least I got something out of it by seeing all what find_theorems returns about Int.plus_int* above.
What is a quotient type?
A quotient type is a way to define a new type in terms of an already existing type. That way, we don't have to axiomatize the new type. For example, one might find reasonable to use the naturals to build the integers, since they can be seen as "naturals+negatives". You may then want to use the integers to build the rationals, since they can be seen as "integers+quotients". And so on.
Quotient types use a given equivalence relation on the "lower type" to determine what equality means for the "higher type".
Being more precise: A quotient type is an abstract type for which equality is dictated by some equivalence relation on its underlying representation.
This definition might be too abstract at first, so we'll use the integers as a grounding example.
Example: Integers from Naturals
If one wants to define the integers, the most standard way is to use an ordered pair of natural numbers, such as (a,b), which intuitively represents "a-b". For example, the number represented by the pair (2,4) is -2, since intuitively 2-4 = -2. By the same logic, (0,2) also represents '-2', and so does (1,3) or (10,12), since 0-2 = 1-3 = 10-12 = -2.
We could then say that "two pairs (a,b) and (x,y) represent the same integer iff a - b = x - y". However, the minus operation can be weird in natural numbers (what is '2-3' in the naturals?). To avoid that weirdness, rewrite 'a - b = x - y' as 'a + y = x + b', now using only addition. So, two pairs (a,b) and (x,y) represent the same integer when 'a + y = x + b'. For example, (7,9) represents the same integer as (1,3), since '7 + 3 = 1 + 9'.
That leads to a quotient definition of integers: An integer is a type represented by an ordered pair of natural numbers. Two integers represented by (a,b) and (x,y) are equal if, and only if, a+y = x+b.
The integer type derives from the type "ordeded pair of natural numbers" which is its representation. We may call the integer itself an abstraction of that. The equality of integers is defined as whenever some underlying representations '(a,b)' and '(x,y)' follow the equivalence relation 'a+y = x+b'.
In that sense, the integer '-3' is represented by both '(0,3)' and '(2,5)', and we may show this by noticing that 0+5 = 3+2. On the other hand, '(0,3)' and '(6,10)' do not represent the same integer, since '0+10 ≠ 3+6'. This reflects the fact that '-3 ≠ -4'.
Technically speaking, the integer '-3' is not specifically '(0,3)', nor '(1,4)', nor '(10,13)', but the whole equivalence class. By that I mean that '-3' is the set containing all of its representations (i.e. -3 = { (0,3), (1,4), (2,5), (3,6), (4,7), ... }). '(0,3)' is called a representation for '-3', and '-3' is the abstraction of '(0,3)'.
Morphisms: Rep and Abs in Isabelle
Rep and Abs are ways for us to transition between the representations and the abstractions they represent. More precisely, they are mappings from an equivalence class to one of its representations, and vice-versa. We call them morphisms.
Rep takes an abstract object (an equivalence class), such as '-3', and transforms it into one of its representations, for example '(0,3)'. Abs does the opposite, taking a representation such as '(3,10)', and mapping it into its abstract object, which is '-7'. Int.thy (Isabelle's implementation of integers) defines these as Rep_Integ and Abs_Integ for integers.
Notice that the statement '(2,3) = (8,9)' is an absurd. Since these are ordered pairs, that would imply '2 = 8' and '3 = 9'. On the other hand the statement 'Abs_Integ(2,3) = Abs_Integ(8,9)' is very much true, as we are simply saying that the integer abstraction of '(2,3)' is the same as the integer abstraction '(8,9)', namely '-1'.
A more precise phrasing of 'Abs_Integ(2,3) = Abs_Integ(8,9)' is: "'(2,3)' and '(8,9)' belong in the same equivalence class under the integer relation". We usually call this class '-1'.
It's important to note that '-1' is just a convenient shorthand for "the equivalence class of (0,1)", in the same vein that '5' is just a shorthand for "the equivalence class of (5,0)" and '-15' is shorthand for "the equivalence class of '(0,15)'. We call '(0,1)', '(5,0)', and '(0,15) the canonical representations. So saying "Abs_Integ(2,3) = -1" is really just a nice abbreviation for "Abs_Integ(2,3) = Abs_Integ(0,1)" .
It's also worth noting that the mapping Rep is one-to-one. This means that Rep_Integ(-1) will always yield the same representation pair, usually the canonical '(0,1)'. The specific pair picked does not matter much, but it'll always pick the same one. That is useful to know, as it implies that the statement Rep_Integ(i) = Rep_Integ(i) is always true.
The quotient_type command in Isabelle
'quotient_type' creates a quotient type using the specified type and equivalence relation. So quotient_type int = "nat × nat" / "intrel" creates the quotient type int, as the equivalence classes of nat × nat under the relation intrel (where "intrel = (λ(a,b) (x,y). a+y = x+b)"). Section 11.9.1 of the manual details the specifics about the command.
It's worth noting that you actually have to prove that the relation provided (intrel) is an equivalence.
Here's a usage example from Int.thy, which defines the integers, it's morphisms, and proves that intrel is an equivalence relation:
(* Definition *)
quotient_type int = "nat × nat" / "intrel"
morphisms Rep_Integ Abs_Integ
(* Proof that 'intrel' is indeed an equivalence *)
proof (rule equivpI)
show "reflp intrel" by (auto simp: reflp_def)
show "symp intrel" by (auto simp: symp_def)
show "transp intrel" by (auto simp: transp_def)
qed
Definitions and Lemmas: The Lifting and Transfer packages
Now, the previous explanations suggest that Rep and Abs should appear everywhere, right? These transformations are crucial for proving properties about quotient types. However, they appear less than 10 times throughout the 2000 lines of Int.thy. Why?
lift_definition and the proof method transfer are the answer. They come from the Lifting and Transfer packages. These packages do a lot, but for our purposes, they do the job of concealing Rep and Abs from your definitions and theorems.
The gist when working with quotient types in Isabelle, is that you want to [1] define some operations, [2] prove some useful lemmas with the representation type, and then [3] completely forget about these representations, working only with the abstract type. When proving theorems about the abstract type, you should be using the previously shown properties and lemmas.
To get [1], lift_definition helps you to define the operations. In specific, it allows you to define a function with the representation type, and it automatically "lifts" it to the abstract type.
As an example, you can define addition on integers as such:
lift_definition int_plus:: "int ⇒ int ⇒ int"
is "λ(a,b)(c,d). (a+c, b+d)"
This definition is stated in terms of nat × nat ⇒ nat × nat ⇒ nat × nat, but 'lift_definition' will automatically "lift" it to int ⇒ int ⇒ int.
An important thing to note is that you have to prove the function still follows the equivalence relation after applied (i.e. if 'x ≃ y' then 'f x ≃ f y'). The definition above for example, will prompt you to prove that "if '(a,b) ≃ (x,y)' and '(c,d) ≃ (u,v)', then '(a+c,b+d) ≃ (x+u,y+v)'" (if it doesn't look like it, try using apply clarify).
One of the nice things about lift_definition is that it works in terms of the underlying representation only, so you don't have to worry about transitioning between abstractions and representations. Hence the lack of Rep_Integ and Abs_Integ in Int.thy.
It also sets up a transfer rule for the function. This how you get [2]: proving properties without having to worry about Rep and Abs. Using the transfer proof method, you can bring a lemma about an abstraction down to the representation level, and prove the desired property there.
As an example, you can state the commutativity of addition in the form int_plus x y = int_plus y x, and then use the transfer method to bring that statement down to the representation level, which after a clarify looks like intrel (a + c, b + d) (c + a, d + b). We can then prove by simplification with the definition of intrel:
lemma plus_comm: "int_plus x y = int_plus y x"
apply transfer
apply clarify
by (simp add: intrel_def)
And to get [3], you simply use these lemmas and properties of the abstract type, without worrying about the actual representations.
After this point, you'll even forget that you're using a quotient type, since the abstract type and it's properties are all you need. Usually a handful of lemmas on the abstract type is enough, and Int.thy will give you a lot more than a handful.
References and further reading
Section 1 of the paper "Quotient Types" gives a good overview of the topic (and goes in depth in the other sections).
The introduction of "Quotients Revisited for Isabelle/HOL" also explains very well the purpose of 'Rep' and 'Abs'.
"Lifting and Transfer" is also a great read into how these can be concealed and the automation behind quotient types in Isabelle.
Isabelle's Reference Manual (with some ctrl+f) is also a great source when in doubt about what specific commands do.

Simplify pretty-printing of naturals

Let's say I wrote a function for reversing a list. I want to test it out using the value command, just to assure myself that I probably got it right. But the output looks horrible:
value "reverse [1,8,3]"
> "[1 + 1 + 1, 1 + 1 + (1 + 1) + (1 + 1 + (1 + 1)), 1]" :: "'a list"
If I tell Isabelle to treat those numeric characters as naturals, the output is even worse:
value "reverse [1::nat,8,3]"
> "[Suc (Suc (Suc 0)), Suc (Suc (Suc (Suc (Suc (Suc (Suc (Suc 0))))))), Suc 0]" :: "nat list"
Sometimes I resort to using strings, but that's looks a bit funny with all those apostrophes everywhere:
value "reverse [''1'',''8'',''3'']"
> "[''3'', ''8'', ''1'']" :: "char list list"
Can I instruct Isabelle's pretty-printer to print Suc (Suc (Suc 0)) as 3, and so on? Perhaps by giving some magical incantation to the syntax or translations commands?
Here's my complete example, in case you'd like to paste it into Isabelle:
theory Scratch imports Main begin
fun reverse where
"reverse [] = []"
| "reverse (x#xs) = reverse xs # [x]"
value "reverse [1,8,3]"
value "reverse [1::nat,8,3]"
value "reverse [''1'',''8'',''3'']"
end
Short answer: My first thought is to use type int, since (unlike nat) its code generator setup uses a binary numeral representation by default.
Importing "~~/src/HOL/Library/Code_Target_Nat", as naT suggests, is also a good idea if you don't want to use the Suc representation for type nat.
Explanation: Numerals in Isabelle are encoded using constructors defined in Num.thy; e.g. 5 is an abbreviation for numeral (Bit1 (Bit0 One)). Here One, Bit0 and Bit1 are constructors of type num. numeral is overloaded, and works for any type with a 1 and an associative +. Here are the code equations for numeral:
lemma numeral_code [code]:
"numeral One = 1"
"numeral (Bit0 n) = (let m = numeral n in m + m)"
"numeral (Bit1 n) = (let m = numeral n in m + m + 1)"
If we generate code for 5::'a::numeral, then 1 and + on type 'a are treated as uninterpreted constants, so they remain in the output: (1 + 1) + (1 + 1) + 1.
Generating code for 5::nat works the same, except we do have code for 1 and + on type nat, in terms of Suc. Thus (1 + 1) + (1 + 1) + 1 reduces further to Suc (Suc (Suc (Suc (Suc 0)))).
Type int works differently. The code generator setup in Int.thy uses three constructor functions for type int: Pos and Neg of type num => int, as well as 0. A code_abbrev declaration causes each occurrence of numeral at type num => int to be replaced by Pos during code generation. After the code is run, Pos is then turned back into numeral before Isabelle displays the result. Thus 5::int evaluates to just 5.
Special code setup theories: src/HOL/Library contains a few different theories for customizing code generation for numerals.
"~~/src/HOL/Library/Code_Target_Nat" tells the code generator to use the target language's (e.g. SML or Haskell's) built-in numerals for type nat. For example, 5::nat is usually translated to SML as numeral (Bit1 (Bit0 One)); however, with this library loaded it gets translated as 5 in SML. Results of value are translated back into the Isabelle numeral representation afterward.
"~~/src/HOL/Library/Code_Target_Int" is the same, but for type int instead of nat.
"~~/src/HOL/Library/Code_Target_Numeral" simply loads both of the previous two libraries. It only affects types nat and int, not any other types in class numeral.
"~~/src/HOL/Library/Code_Binary_Nat" configures nat in the same style as the default code setup for int: with constructors 0 and nat_of_num::num => nat and a code_abbrev declaration. With this library, value "5::nat" also returns 5.
Note: I hope my answer here doesn't prevent Brian Huffman or Florian Haftmann from giving an answer. If it does, that would be a bad thing. Hopefully, I'm just doing some gofer work to set either of them up.
Short answer 1: The pertinent mailing list email is Re: [isabelle] value no longer pretty-prints numbers of type nat.
Short answer 2: A solution for nat is to import "~~/src/HOL/Library/Code_Target_Nat". Because I'm not clear on the details of how numeral and num are completely tied into HOL at the low level, the solution I'm giving you is not necessarily a good and final solution.
A big part of my answer here is partly to say, before Brian Huffman or Florian Haftmann get here, who are the authors of Num.thy, "I'm interested in this too, because it's related to numeral, which is a powerful part of HOL. The more info I have about the intricacies of using numeral, the better".
Basically, they made a design choice change for Isabelle2013-1, which is to have the default for nat and numeral be represented in a successor form. That's what the mailing list email is about.
If you use declare[[show_sorts=true]], you will see that your value "reverse [1,8,3]" is using type class numeral. I mention that because I've been putting a lot of effort into trying to learn about numeral, and even with concrete types, such as nat and int, the use of constants such as 44 and 5 involve numeral, at least for the input. And even with a concrete type like nat, numeral can be involved in simp rules that are being used.
Before saying more, one way to get nice nat output for value "reverse [1::nat,8,3]" is, again, to use the following import:
"~~/src/HOL/Library/Code_Target_Nat"
The reason I'm interested in your question is because that's just a plug-n-play solution I saw by Andreas Lochbihler in that email.
I can't get value "reverse [1,8,3]" to not use sums of 1 by importing this:
"~~/src/HOL/Library/Code_Target_Numeral"
So, I want to know about how to get numeral to be in that nice form we love to see.
Anyway, numeral is at the core of using number constants. Consider this:
lemma "(3::nat) = z"
using[[simp_trace, simp_trace_depth_limit=100, linarith_trace, rule_trace]]
using[[blast_trace, blast_stats]]
apply simp
oops
Part of the simp trace is this:
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
3 = z
[1]Procedure "Num.reorient_numeral" produced rewrite rule:
?a1 = ?b1 ≡ ?b1 = ?a1
[1]Applying instance of rewrite rule
?a1 = ?b1 ≡ ?b1 = ?a1
[1]Rewriting:
3 = z ≡ z = 3
If you look at simp traces involving number constants, you'll see that Num rules show up a lot, which come from that most excellent of Num.thy.

Resources