"String of nat" in Isabelle? - isabelle

I'm struggling to find a function of type nat => string that converts terms like
42
into terms like
''42''
Does it exist? I've found char_of_nat (in the String library) but that's a bit too low-level, being concerned with ASCII codes and the like.

In the archive of formal proofs, under Real_Impl/Show you find a class show with a function essentially of type 'a => string. In Real_Impl/Show_Instances several common types are instantiated, including nat, rat, and int.

In the meantime, I've gone ahead and written my own string_of_nat and string_of_int functions. In the absence of other pre-existing functions, these will suit me fine.
fun string_of_nat :: "nat ⇒ string"
where
"string_of_nat n = (if n < 10 then [char_of_nat (48 + n)] else
string_of_nat (n div 10) # [char_of_nat (48 + (n mod 10))])"
definition string_of_int :: "int ⇒ string"
where
"string_of_int i = (if i < 0 then ''-'' # string_of_nat (nat (- i)) else
string_of_nat (nat i))"

There's a Haskell-like show class in the AFP entry Show.
The instance for the nat type is in Show.Show_Instances.
Example:
theory Show_Test
imports "Show.Show_Instances"
begin
lemma "show (123 :: nat) = ''123''"
by (simp add: Show_Instances.shows_prec_nat_def showsp_nat.simps shows_string_def)
end

Related

How can i cast nat to int in Isabelle

While doing my homework I got stuck in the following question:
Create the function add. It returns the sum of two natural numbers.
Prove that it works.
I'm stuck because to write the code to prove it, I need to cast a natural number (nat in Isabelle) to int.
Here's the add function that I wrote:
primrec add :: "nat ⇒ nat ⇒ nat"
where
add01: "add x 0 = x" |
add02: "add x (Suc y) = Suc (add x y) "
To know the result I did this:
value "somaNat (Suc(Suc(0))) (Suc(0))"
It returns 3, as it should.
Suc(Suc(0) = 2
Suc(0) = 1
Also tried to create a function that cast it to int, like this:
primrec nat_to_int :: "nat ⇒ int"
where
nat_to_int02: "nat_to_int x = value x"
It does not work because (value x) can't be on the right side.
I searched for it in the tutorial that Isabelle provides, and in another one, that I found online.
The closest question I found on SO was this one:
Casting int to nat in Isabelle
So, how can I cast nat to int in Isabelle?

Defining functions between constants in Isabelle

I'm a mathematician just starting to get used to Isabelle, and something that should be incredibly simple turned out to be frustrating. How do I define a function between two constants? Say, the function f: {1,2,3} \to {1,2,4} mapping 1 to 1, 2 to 4 and 3 to 2?
I suppose I managed to define the sets as constants t1 and t2 without incident, but (I guess since they're not datatypes) I can't try something like
definition f ::"t1 => t2" where
"f 1 = 1" |
"f 2 = 4" |
"f 3 = 2"
I believe there must be a fundamental misconception behind this difficulty, so I appreciate any guidance.
There's a number of aspects to your question.
First, to get something working quickly, use the fun keyword instead of definition, like so:
fun test :: "nat ⇒ nat" where
"test (Suc 0) = 1" |
"test (Suc (Suc 0)) = 4" |
"test (Suc (Suc (Suc 0))) = 2" |
"test _ = undefined"
You cannot pattern match on any arguments directly in the head of the definition using the definition keyword, whereas you can with fun. Note also that I have replaced the overloaded numeric literals (1, 2, 3, etc.) with the constructors for the nat datatype (0 and Suc) in the pattern match.
An alternative would be to stick with definition, but push the case analysis of the function's argument inside the body of the definition using a case statement, like so:
definition test2 :: "nat ⇒ nat" where
"test2 x ≡
case x of
(Suc 0) ⇒ 1
| (Suc (Suc 0)) ⇒ 4
| (Suc (Suc (Suc 0))) ⇒ 2
| _ ⇒ undefined"
Note that definitions like test2 are not unfolded by the simplifier by default, and you will need to manually add the theorem test2_def to the simplifier's simpset if you want to expand occurrences of test2 in a proof.
You can also define new types (you cannot use sets as types, directly, as you are trying to do) corresponding to your two three-element sets with typedef, but personally I would stick with nat.
EDIT: to use typedef, do something like:
typedef t1 = "{x::nat. x = 1 ∨ x = 2 ∨ x = 3}"
by auto
definition test :: "t1 ⇒ t1" where
"test x ≡
case (Rep_t1 x) of
| Suc 0 ⇒ Abs_t1 1
| Suc (Suc 0) ⇒ Abs_t1 4
| Suc (Suc (Suc 0)) ⇒ Abs_t1 2"
Though, I don't really ever use typedef myself, and so this may not be the best way of using this and others may possibly suggest some other way. What typedef does is carve out a new type from an existing one, by identifying a non-empty set of inhabitants for the new type. The proof obligation, here closed by auto, is merely to demonstrate that the defining set for the new type is indeed non-empty, and in this case I am carving out a three-element set of naturals into a new type, called t1, so the proof is fairly trivial. Two new constants are created, Abs_t1 and Rep_t1 which allow you to move back-and-forth between the naturals and the new type. If you put a print_theorems after the typedef command you will see several new theorems about t1 that Isabelle has automatically generated for you.

Isabelle: linord proof

My attempt to create a custom linear order for a custom data type failed, Below is my code:
theory Scratch
imports Main
begin
datatype st = Str "string"
fun solf_str_int:: "string ⇒ int" where
"solf_str_int str = (if (size str) > 0
then int(nat_of_char (hd str) + 1) + 100 * (solf_str_int (tl str))
else 0)"
fun soflord:: "st ⇒ st ⇒ bool" where
"soflord s1 s2 = (case s1 of Str ss1 ⇒ (case s2 of Str ss2 ⇒
(solf_str_int ss1) ≤ (solf_str_int ss2)))"
instantiation st :: linorder
begin
definition nleq: "less_eq n1 n2 == soflord n1 n2"
definition neq: "eq n1 n2 == (n1 ≤ n2) ∧ (n2 ≤ n1)"
definition nle: "less n1 n2 == (n1 ≤ n2) ∧ (¬(n1 = n2))" (* ++ *)
instance proof
fix n1 n2 x y :: st
show "n1 ≤ n1" by (simp add:nleq split:st.split)
show "(n1 ≤ n2) ∨ (n2 ≤ n1)" by (simp add:nleq split:st.split) (*why is 'by ()' highlited?*)
(*this fail if I comment line ++ out*)
show "(x < y) = (x ≤ y ∧ (¬ (y ≤ x)))" by (simp add:nleq neq split:node.split)
qed
end
end
The definition marked with (* ++ *) is not right and if delete it the last show give problems.
How do I correct the prove?
Why is the second last show partially highlighted?
When you define the operations of a type class (less_eq and less in the case of linorder), the name of the overloaded operation can only be used if the inferred type of the operation matches exactly the overloaded instance that is being defined. In particular, the type is not specialised if it turns out to be too general.
The definition for less_eq works because soflord restricts the types of n1 and n2 to st, so less_eq is used with type st => st => bool, which is precisely what is needed here. For less, type inference computes the most general type 'b :: ord => 'b => bool. As this is not of the expected type st => st => bool, Isabelle does not recognize the definition as a definition of an overloaded operation and consequently complains that you want to redefine an existing operation in its full generality. If you restrict the types as necessary, then the definition works as expected.
definition nle: "less n1 (n2 :: st) == (n1 ≤ n2) ∧ (¬(n1 = n2))"
However, your definitions do not define a linear order on st. The problem is that antisymmetry is violated. For example, the two strings Str ''d'' and Str [Char Nibble0 Nibble0, Char Nibble0 Nibble0] (i.e., the string consisting of two characters at codepoint 0) are "equivalent" in your order, although they are different values. You attempt to define equality on st, too, but in higher-order logic, equality cannot be defined. It is determined by the way you constructed your type. If you really want to identify strings that are equivalent according to your order, you have to construct a quotient first, e.g., using the quotient package.
The purple highlighting of by(simp ...) indicates that the proof method simp is still running. In your case, it will not terminate, because simp will keep unfolding the defining equation for solf_str_int: its right-hand side contains an instance of the left-hand side. I recommend that you define your functions by pattern-matching on the left-hand side of =. Then, the equations are only used when they can consume a pattern. Thus, you have to trigger case distinctions yourself (e.g. using cases), but you also get more control over the automated tactics.

Isabelle type unification/inference error

I'm just getting started in Isabelle and I'm getting a type unification error while working through Exercise 3.3 in of Concrete Semantics:
Define a substitution function
subst :: vname ⇒ aexp ⇒ aexp ⇒ aexp
such that subst x a e is the result of replacing every occurrence of variable x by a in e. For example:
subst ''x'' (N 3) (Plus (V ''x'') (V ''y'')) = Plus (N 3) (V ''y'')
Here's what I've got so far:
theory Scratchpad
imports Main
begin
type_synonym vname = string
type_synonym val = int
type_synonym state = "vname ⇒ val"
datatype aexp = N int | V vname | Plus aexp aexp
fun subst :: "vname ⇒ aexp ⇒ aexp ⇒ aexp" where
"subst x (N a) (N e) = (N e)" |
"subst x (N a) (V e) = (if x=e then (N a) else (V e))" |
"subst x (N a) (Plus e1 e2) = Plus(subst(x (N a) e1) subst(x (N a) e2))"
end
When the third case in the function definition is commented out, running the test cases
value "subst ''x'' (N 3) (N 5)"
value "subst ''x'' (N 3) (V ''x'')"
produces (N 5) and (N 3) respectively, so I know the first two lines are working correctly. Adding the last line results in the error
Type unification failed: Clash of types "_ ⇒ _" and "_ list"
Type error in application: operator not of function type
Operator: x :: char list
Operand: N a :: aexp
I don't think this is a syntax issue, although I'm not yet completely sure what purposes different types of quotation marks serve (e.g. double quotes vs. two single quotes). From this answer, I believe that Isabelle is assigning x to be a function type on the right side of the line, which is not what I want.
What do the error messages actually mean (specifically and generally), and how do I fix this?
To answer your question about quotes: Two single quotes are used in Isabelle/HOL (more precisely its inner syntax) to denote string literals. That is, by ''abc'' we denote the string containing the three characters a, b, and c (which would again use some special syntax if you had to enter them literally). Double quotes on the other hand, are mostly used to separate Isar statements (outer syntax) from terms inside the logic. So while ''...'' is part of the term language, "..." is not.
Now for the error message. It tells you that you are trying to use the list x (type _ list) as a function (type _ => _). Why does Isabelle think you want to use x as a function? Well, because juxtaposition (i.e., writing terms next to each other, separated by white space) denotes function application. Thus x (N a) is interpreted as applying a function x to an argument (N a) (just as f y is the application of f to the argument y). In order to give your definition the correct meaning you have to use parenthesis at the right positions. I guess what you intended in your third clause was:
Plus (subst x (N a) e1) (subst x (N a) e2)
where we have two occurrences of the function subst applied to three arguments. (So it was a syntax issue after all ;).)
Another comment. Your implementation of subst could be more general. As is, the second argument of subst is always fixed to be some number a (because of your usage of the constructor N). However, everything should work just as well if you would allow arbitrary expressions of type aexp.

Unfold/simp has no effect in a primrec type class instantiation proof

Up until several days ago, I always defined a type, and then proved theorems directly about the type. Now I'm trying to use type classes.
Problem
The problem is that I can't instantiate cNAT for my type myD below, and it appears it's because simp has no effect on the abstract function cNAT, which I've made concrete with my primrec function cNAT_myD. I can only guess what's happening because of the automation that happens after instance proof.
Questions
Q1: Below, at the statement instantiation myD :: (type) cNAT, can you tell me how to finish the proof, and why I can prove the following theorem, but not the type class proof, which requires injective?
theorem dNAT_1_to_1: "(dNAT n = dNAT m) ==> n = m"
assumes injective: "(cNAT n = cNAT m) ==> n = m"
Q2: This is not as important, but at the bottom is this statement:
instantiation myD :: (type) cNAT2
It involves another way I was trying to instantiate cNAT. Can you tell me why I get Failed to refine any pending goal at shows? I put some comments in the source to explain some of what I did to set it up. I used this slightly modified formula for the requirement injective:
assumes injective: "!!n m. (cNAT2 n = cNAT2 m) --> n = m"
Specifics
My contrived datatype is this, which may be useful to me someday: (Update: Well, for another example maybe. A good mental exercise is for me to try and figure out how I can actually get something inside a 'a myD list, other than []. With BNF, something like datatype_new 'a myD = myS "'a myD fset" gives me the warning that there's an unused type variable on the right-hand side)
datatype 'a myD = myL "'a myD list"
The type class is this, which requires an injective function from nat to 'a:
class cNAT =
fixes cNAT :: "nat => 'a"
assumes injective: "(cNAT n = cNAT m) ==> n = m"
dNAT: this non-type class version of cNAT works
fun get_myL :: "'a myD => 'a myD list" where
"get_myL (myL L) = L"
primrec dNAT :: "nat => 'a myD" where
"dNAT 0 = myL []"
|"dNAT (Suc n) = myL (myL [] # get_myL(dNAT n))"
fun myD2nat :: "'a myD => nat" where
"myD2nat (myL []) = 0"
|"myD2nat (myL (x # xs)) = Suc(myD2nat (myL xs))"
theorem left_inverse_1 [simp]:
"myD2nat(dNAT n) = n"
apply(induct n, auto)
by(metis get_myL.cases get_myL.simps)
theorem dNAT_1_to_1:
"(dNAT n = dNAT m) ==> n = m"
apply(induct n)
apply(simp) (*
The simp method expanded dNAT.*)
apply(metis left_inverse_1 myD2nat.simps(1))
by (metis left_inverse_1)
cNAT: type class version that I can't instantiate
instantiation myD :: (type) cNAT
begin
primrec cNAT_myD :: "nat => 'a myD" where
"cNAT_myD 0 = myL []"
|"cNAT_myD (Suc n) = myL (myL [] # get_myL(cNAT_myD n))"
instance
proof
fix n m :: nat
show "cNAT n = cNAT m ==> n = m"
apply(induct n)
apply(simp) (*
The simp method won't expand cNAT to cNAT_myD's definition.*)
by(metis injective)+ (*
Metis proved it without unfolding cNAT_myD. It's useless. Goals always remain,
and the type variables in the output panel are all weird.*)
oops
end
cNAT2: Failed to refine any pending goal at show
(*I define a variation of `injective` in which the `assumes` definition, the
goal, and the `show` statement are exactly the same, and that strange `fails
to refine any pending goal shows up.*)
class cNAT2 =
fixes cNAT2 :: "nat => 'a"
assumes injective: "!!n m. (cNAT2 n = cNAT2 m) --> n = m"
instantiation myD :: (type) cNAT2
begin
primrec cNAT2_myD :: "nat => 'a myD" where
"cNAT2_myD 0 = myL []"
|"cNAT2_myD (Suc n) = myL (myL [] # get_myL(cNAT2_myD n))"
instance
proof (*
goal: !!n m. cNAT2 n = cNAT2 m --> n = m.*)
show
"!!n m. cNAT2 n = cNAT2 m --> n = m"
(*Failed to refine any pending goal
Local statement fails to refine any pending goal
Failed attempt to solve goal by exported rule:
cNAT2 (n::nat) = cNAT2 (m::nat) --> n = m *)
Your function cNAT is polymorphic in its result type, but the type variable does not appear among the parameters. This often causes type inference to compute a type which is more general than you want. In your case for cNAT, Isabelle infers for the two occurrences of cNAT in the show statement the type nat => 'b for some 'b of sort cNAT, but their type in the goal is nat => 'a myD. You can see this in jEdit by Ctrl-hovering over the cNAT occurrences to inspect the types. In ProofGeneral, you can enable printing of types with using [[show_consts]].
Therefore, you have to explicitly constrain types in the show statement as follows:
fix n m
assume "(cNAT n :: 'a myD) = cNAT m"
then show "n = m"
Note that it is usually not a good idea to use Isabelle's meta-connectives !! and ==> inside a show statement, you better rephrase them using fix/assume/show.

Resources