Isabelle type unification/inference error - isabelle

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.

Related

Defining Primtive Recursion for multiplication in Isabelle

I am new to Isabelle and I am trying to define primitive recursive functions. I have tried out addition but I am having trouble with multiplication.
datatype nati = Zero | Suc nati
primrec add :: "nati ⇒ nati ⇒ nati" where
"add Zero n = n" |
"add (Suc m) n = Suc(add m n)"
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Suc(Zero) n = n" |
"mult (Suc m) n = add((mult m n) m)"
I get the following error for the above code
Type unification failed: Clash of types "_ ⇒ _" and "nati"
Type error in application: operator not of function type
Operator: mult m n :: nati
Operand: m :: nati
Any ideas?
The problem is your mult function: It should look like this:
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Zero n = Zero" |
"mult (Suc m) n = add (mult m n) m"
Function application in functional programming/Lambda calculus is the operation that binds strongest and it associates to the left: something like f x y means ‘f applied to x, and the result applied to y’ – or, equivalently due to Currying: the function f applied to the parameters x and y.
Therefore, something like mult Suc(Zero) n would be read as mult Suc Zero n, i.e. the function mult would have to be a function taking three parameters, namely Suc, Zero, and n. That gives you a type error. Similarly, add ((mult m n) m) does not work, since that is identical to add (mult m n m), which would mean that add is a function taking one parameter and mult is one taking three.
Lastly, if you fix all that, you will get another error saying you have a non-primitive pattern on the left-hand side of your mult function. You cannot pattern-match on something like Suc Zero since it is not a primitive pattern. You can do that if you use fun instead of primrec, but it is not what you want to do here: You want to instead handle the cases Zero and Suc (see my solution). In your definition, mult Zero n would even be undefined.

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 recursive function

I have the following recursive function that creates a list of 0s (i.e. [0,...,0]) in VDM. How can this be translated to Isabelle using fun-where?
VDM:
NewList: nat1 * seq of nat -> seq of nat
NewList(n, l) ==
if len l = n then l
else NewList(n, l ^ [0])
-- pre/post-conditions excluded here
My attempts are horribly wrong due to my lack of understanding of Isabelle (but below at least proves that I tried...).
Isabelle:
fun
NewList:: "N ⇒ (VDMNat VDMSeq) ⇒ (VDMNat VDMSeq)"
where
"NewList n [] = NewList n [0]"
| "NewList n [x] = (if len [x] = n then [x] else NewList n (x#[0]))"
| "NewList n (x # xs) = (if len (x # xs) = n then (x # xs) else NewList n ((x # xs) # [(0::VDMNat)]))"
*The data types VDMNat and VDMSeq are defined in some library. Please ignore the VDMNat and VDMSeq for now - any sort of implementation using Isabelle's data types are welcome (at least it would provide a good reference for my implementation). Please refer to the VDM code for the data types intended.
Could you also please explain what x, xs, and (x # xs) are referring to? I've seen this in several recursive function examples (though none helps me).
Thank you for your help!
First of all, x and xs are variables. When definiting recursive functions on lists, these are often used to denote the first element of the list (x) and the remaining list (xs). The expression x # xs means ‘x prepended to the list xs’, and that is the reason why (x # xs) # [0] in your question does not work: x # xs is a list and [0] is also a list. You would have to do x # xs # [0}, where # is the function to concatenate two lists.
Now, to your function: My interpretation of your function definition is that you have a natural number n and a list l and want to pad the list l with zeros at the back up to length n.
However, when the list l is of length > n to begin with, your function does not terminate. You would have to think about what to do in that case.
Here are my suggestions for what you could do:
Possibility 1
Change the = n to a ≥ n. Then you can prove termination of the function by looking at
function new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = (if length l ≥ n then l else new_list n (l # [0]))"
by pat_completeness auto
termination by (relation "measure (λ(n, l). n - length l)") auto
However, proving theorems about this will probably get ugly. I would therefore urge you to do something like the following two possibilities. Ideally, use functions from Isabelle's standard library, because there is usually good automation setup for them. Alternatively, define your own small building blocks (like take and replicate) for your datatypes and prove reusable facts on them and combine them to do what you want. A ‘monolithic’ function definition like yours is difficult to work with when doing proofs.
Possibility 2
Use the builtin function replicate, which takes a natural number n and an element and returns a list of n times that element:
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = l # replicate (n - length l) 0"
You can also do the same thing with fun, but definition is the more low-level tool. Note that definition does not add the function definition theorem new_list_def as a simplifier rule; you can do this by writing declare new_list_def [simp].
Possibility 3
You can combine possibility 2 with the builtin function take to ensure that you always get a list of length exactly n, even when the input list is longer (it is then possibly truncated):
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = take n l # replicate (n - length l) 0"
Summary
In the first two cases, you can prove the theorems
length l ≤ n ⟹ length (new_list n l) = n
take (length l) (new_list n l) = l
(in the first case by induction using new_list.induct; in the second case just by unfolding the definition and simplifying)
In the third case, you can prove
length (new_list n l) = n
take (length l) (new_list n l) = take n l
Obviously, if length l ≤ n, the first two and the last one coincide completely.
The easy solution is: replicate n (0::nat) using the function replicate of Isabelle/HOL's library.
If you want to implement the function yourself via fun then do what you should always do in functional programming ;) try to split your problem into smaller problems that can be solved recursively:
fun newlist :: "nat => nat list"
where
"newlist 0 = []" -- "the only list of length 0*)
| "newlist (Suc n) = ..." -- "use result for 'n' to obtain result for 'n+1'"

"String of nat" in 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

Isabelle: Evaluating formula with Quantifiers

Why does the following work:
fun f :: "nat ⇒ bool" where
"f _ = (True ∨ (∀x. x))"
But this fails
fun g :: "nat ⇒ bool" where
"g _ = (True ∨ (∀a. True))"
with
Additional type variable(s) in specification of "g_graph": 'a
Specification depends on extra type variables: "'a"
The error(s) above occurred in "test.g_sumC_def"
The error(s) above occurred in definition "g_sumC_def":
"g_sumC ≡ λx. THE_default undefined (g_graph TYPE('a) x)"
Similarly, the following succeeds,
value "True ∨ (∀x. x)"
but this fails
value "True ∨ (∀x. True)"
with
Wellsortedness error:
Type 'a not of sort enum
Cannot derive subsort relation {} < enum
The short answer is: In your first function definition type inference easily infers that x is of type bool, while in your second definition, the bound variable a is not used anywhere else and thus its type is arbitrary ('a). Which is what Additional type variable(s) in specification ... expresses.
If you constrain the type of a explicitly, e.g.,
fun g :: "nat ⇒ bool" where
"g _ = (True ∨ (∀a::bool. True))"
the function definition is accepted.
A longer answer: Since the definition of g is not recursive you could turn it into using definition instead of fun. Then your first attempt does not fail completely but the result might surprise you. After
definition g :: "nat ⇒ bool" where
"g _ = (True ∨ (∀a. True))"
the type of g is 'a itself => nat => bool instead of the intended nat => bool. The reason is the same as for the failure of fun before. Since a is of arbitrary type, this additional type has to be recorded in the type of g, which is done by introducing an additional dummy argument which just states this additional type explicitly. Here 'a itself is a type whose constructor TYPE(...) -- taking a type as argument -- allows us to encode type information on the term level. E.g.,
TYPE('a) :: 'a itself
TYPE(bool) :: bool itself
TYPE(nat) :: nat itself
Then g TYPE(nat) is the version of g where a is fixed to be of type nat.
Concerning your value statements, the reason for the second one to fail is not really related to the above. In the first statement the universal quantifier binds a variable of type bool whose values can be enumerated explicitly and thus a result can be computed by considering all those values. In contrast, in your second statement the bound variable x is of an arbitrary type 'a whose values cannot be enumerated explicitly.
The following fails:
fun f where "f _ = (∀a. True)"
because the type of a has hidden polymorphism (i.e., there is a type variable inside your function's body that is not present in the function's type signature), which upsets the function package's internal proofs.
If you explicitly give a a type as so:
fun f where "f _ = (∀a::bool. True)"
or is you give a a type that is also in the function's type signature, as so:
fun f where "f _ = (∀a::bool. True)"
the function definition succeeds. Your example:
fun f where "f _ = (∀x. x)"
succeeds, because x is forced to be type bool.
As for your value commands, Isabelle attempts to generate executable code for your expression, and hence needs to not only know the type of your for-all statements, but also be able to enumerate all possible values of it, so that it can test them all. Types such as bool work fine, but type variables like 'a or infinite types such as nat are not enumerable, and hence Isabelle cannot generate code for them.

Resources