I want to use the coprime function that is defined in Isabelle's GCD and play a bit around with it.
Why does value "coprime Suc(Suc 0) Suc(Suc(Suc (Suc 0)))" return the error
Type unification failed: No type arity fun :: gcd
Type error in application: incompatible operand type
Operator: coprime :: ??'a ⇒ ??'a ⇒ bool
Operand: Suc :: nat ⇒ nat
Coercion Inference:
Local coercion insertion on the operand failed:
No type arity fun :: gcd
Now trying to infer coercions globally.
Coercion inference failed:
weak unification of subtype constraints fails
Clash of types "_ ⇒ _" and "nat"
instead of false ?
(This is also the case for value "coprime 0 0".)
A minimal MWE w.r.t the answer:
(*<*) theory T
imports
Main
"~~/src/HOL/Number_Theory/Number_Theory"
begin (*>*)
value "coprime 2 (4 :: nat))"
(*<*) end (*>*)
There are a number of issues here.
It should be value "coprime (Suc(Suc 0)) (Suc(Suc(Suc (Suc 0))))". Function application binds strongest and associates to the left, so what you wrote would be interpreted as coprime applied to Suc, Suc 0, and some other arguments, which is a type error.
coprime 0 0 works out fine in my version of Isabelle; it outputs the somewhat confusing "equal_class.equal (gcd 0 0) 1" :: "bool". The reason for this is that there is nothing in that term to indicate that the 0 is a natural number, and evaluation of polymorphic constants tends to be problematic. Even something like 2 ≠ 4 will not evaluate to True in general because this depends on what type 2 and 4 have. If you write coprime 0 (0::nat), everything works as expected.
Additionally, it would be more convenient to write value "coprime 2 (4 :: nat) instead of using the successor notation.
Related
In Isabelle natural numbers are defined as follows
typedecl ind
axiomatization Zero_Rep :: ind and Suc_Rep :: "ind ⇒ ind"
― ‹The axiom of infinity in 2 parts:›
where Suc_Rep_inject: "Suc_Rep x = Suc_Rep y ⟹ x = y"
and Suc_Rep_not_Zero_Rep: "Suc_Rep x ≠ Zero_Rep"
subsection ‹Type nat›
text ‹Type definition›
inductive Nat :: "ind ⇒ bool"
where
Zero_RepI: "Nat Zero_Rep"
| Suc_RepI: "Nat i ⟹ Nat (Suc_Rep i)"
That's a lot of code to write what's effectively just
datatype nat = Zero | Suc nat
Is there some greater purpose to ind or maybe it is there just for historical reasons?
The datatype package needs a whole lot of maths to do all those internal constructions that are required to give you the datatype you want in the end. In particular, it needs natural numbers.
So the reason why the datatype package is not used to define the naturals is that it simply isn't available yet at that point.
One could of course just axiomatise the nat type directly. I think the idea to instead axiomatise some infinite type and then carve the naturals out of that is a standard one, I think it is done similarly in Zermelo–Fraenkel logic.
Side note: In fact one could even make the datatype package itself axiomatic. But the common philosophy in interactive theorem provers, especially in the LCF family, is to work from a small set of axioms and that everything that can be constructed should be constructed instead of axiomatised. This reduces the amount of "trusted code".
A direct axiomatisation of the natural numbers would not be very controversial I think, but for something as complicated as the datatype package an axiomatic implementation would h introduce lots of possibilities for subtle soundness bugs.
Consider the following property:
lemma "finite {t. (c,s) ⇒ t}"
Which refers to the following big step semantics:
inductive gbig_step :: "com × state ⇒ state ⇒ bool" (infix "⇒" 55)
where
Skip: "(SKIP, s) ⇒ s"
| Assign: "(x ::= a, s) ⇒ s(x := aval a s)"
| Seq: "⟦(c1, s1) ⇒ s2; (c2, s2) ⇒ s3⟧ ⟹ (c1;;c2, s1) ⇒ s3"
| IfBlock: "⟦(b,c) ∈ set gcs; bval b s; (c,s) ⇒ s'⟧ ⟹ (IF gcs FI, s) ⇒ s'"
| DoTrue: "⟦(b,c) ∈ set gcs; bval b s1; (c,s1) ⇒ s2;(DO gcs OD,s2) ⇒ s3⟧
⟹ (DO gcs OD, s1) ⇒ s3"
| DoFalse: "⟦(∀ (b,c) ∈ set gcs. ¬ bval b s)⟧ ⟹ (DO gcs OD, s) ⇒ s"
To me it is obvious that the property holds by induction on the big step relation. However, I can not get it out of the set, so I cannot effectively induct on it.
How could I do this?
Finiteness is nothing that you could prove directly with the induction rule of an inductive predicate. The problem is that looking at an individual run (as does the induction rule) does not say anything about the branching behaviour, which must also be finite for the statement to hold.
I see two approaches to proving finiteness:
Model the derivation tree explicitly as a datatype in Isabelle/HOL and prove that it adequately represent the derivation trees behind inductive. Then prove that the tree has finitely many leaves (by induction on the tree). If you design the datatype such that the states in the leaves are a type parameter, then the corresponding set function generated by the datatype package is what you want to prove to be finite. (Note that you cannot prove finiteness by the induction rule of the set function, because that would again be just a single run.)
Look at the internal construction of the inductive definition. It is defined as the least fixpoint of a functional. You can get access to these internals by putting the inductive definition into a context in which [[inductive_internals]] is declared. Then you can prove that the functional preserves finiteness in a single step and then lift that through the induction.
The proof argument in both approaches is similar. The explicit datatype in #1 simply reifies the fixpoint argument of #2. So you can think of #1 as a deep embedding of #2. Of course, you can also re-derive the internal construction (in a more suitable format) just from the introduction and induction theorems and then follow approach #2.
I would try to do precisely this as your semantics is small. For a large real-world semantics, it might make sense to spend the effort to automate step #2 in ML.
The following code doesn't typecheck:
type_synonym env = "char list ⇀ val"
interpretation map: order "op ⊆⇩m :: (env ⇒ env ⇒ bool)" "(λa b. a ≠ b ∧ a ⊆⇩m b)"
by unfold_locales (auto intro: map_le_trans simp: map_le_antisym)
lemma
assumes "mono (f :: env ⇒ env)"
shows "True"
by simp
Isabelle complains with the following error at the lemma:
Type unification failed: No type arity option :: order
Type error in application: incompatible operand type
Operator: mono :: (??'a ⇒ ??'b) ⇒ bool
Operand: f :: (char list ⇒ val option) ⇒ char list ⇒ val option
Why so? Did I miss something to use the interpretation? I suspect I need something like a newtype wrapper here...
When you interpret a locale like order which corresponds to a type class, you only get the theorems proved inside the context of the locale. However, the constant mono is only defined on the type class. The reason is that mono's type contains two type variables, whereas only one is available inside locales from type classes. You can notice this because there is no map.mono stemming from your interpretation.
If you instantiate the type class order for the option type with None being less than Some x, then you can use mono for maps, because the function space instantiates order with the pointwise order. However, the ordering <= on maps will only be semantically equivalent to ⊆⇩m, not syntactically, so none of the existing theorems about ⊆⇩m will work for <= and vice versa. Moreover, your theories will be incompatible with other people's that instantiate order for option differently.
Therefore, I recommend to go without type classes. The predicate monotone explicitly takes the order to be used. This is a bit more writing, but in the end, you are more flexible than with type classes. For example, you can write monotone (op ⊆⇩m) (op ⊆⇩m) f to express that f is a monotone transformation of environments.
Isabelle doesn't let me write just fun f :: "nat list => nat"; I have to add at least one defining equation, e.g. where "f [] = 5". But since it's fine to leave some constructors undefined, why can't I simply leave all constructors undefined? Then, a fun f without a where would be a handy alternative to the consts f declaration.
Firstly, I do not see why fun f without where is a handy alternative to consts f. You have the latter at your disposal (but also have to specify a type), use it if you must. Moreover, consts is more primitive than commands like definition, fun, and -- in my opinion -- shouldn't be used in production code.
As for your comment on leaving constructors undefined inside a function declaration. The wording "undefined" is misleading here, since, e.g., after
fun f :: "'a list => nat"
where
"f [] = 0"
you can prove that
lemma "f (x#xs) = undefined"
by (cases "x#xs" rule: f.elims) simp
and thus f (x#xs) is, in some sense, defined to be the value undefined. In contrast, after
consts f :: "'a list => nat"
you cannot proof anything about f. You just told the system that there is a constant f of a certain type, but without saying anything further about it.
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.