Defining finite sets in Isabelle - isabelle

How can I define constant sets in Isabelle ? For example something like {1,2,3} (to give it a more interesting twist with 1,2,3 being reals), or {x \in N: x < m}, where m is some fixed number - or, perhaps more difficult, the set {N,R,C}, where N are the naturals numbers, R the real and C the complex ones.
I imagine in all cases it has to be something like
definition a_set :: set
where "a_set ⟷ ??? "
but various attempts of replacing ??? with something correct failed.
Somehow all the tutorial I found talk about defining functions on sets - but I couldn't find simple examples like these to learn from.

The definition command defines a constant. It takes a single equation with the symbol to be defined on the left-hand side, e.g. definition "x = 5" or definition "f = (λx. x + 1)". For increased readability, function arguments can appear on the left-hand side of the equation, e.g. f x = x + 1.
The problem is that you're using ⟷ (the ‘if and only if’ operator, i.e. equality of Booleans). When you have Booleans, it's a good idea to use this instead of simply = because it saves parentheses: You can write ‘P x ⟷ x = 2 ∨ x = 5’ instead of ‘P x = (x = 2 ∨ x = 5)’. (The = operator binds more strongly than the logical connectives ∨ and ∧; ⟷, on the other hand, binds more weakly)
⟷ is just another way of writing = specialised to Booleans. That means that if you're defining something that doesn't return a Boolean, ⟷ is not going to work. Just use regular =:
definition A :: "real set" where
"A = {1, 2, 3}"
Or, for your other example:
definition B :: "complex set set" where
"B = {ℕ, ℝ, UNIV}"
Note that HOL is a typed logic; that means that you cannot just do
definition a_set :: set
because there is no type of all sets. There is only a type of all sets whose elements have a specific type, e.g. nat set or (real ⇒ real) set or indeed nat set set. Just saying set will give you an error message ‘Could not parse type’ because set is a type constructor that expects one type argument and you have given it none.
Regarding the set {ℕ, ℝ, ℂ}, this is the constant B I defined above as an example. There is no ℂ in Isabelle because that is just UNIV :: complex set. (UNIV being the set of all values of the type in question). Note that the ℕ and ℝ in that case are the set of natural and real numbers as a subset of the complex numbers.

Related

How to get the minimum length element in a set, using the comprehension or the lambda function

For some purpose, I want to get the smallest length of element in ('a list set) as the InitalState, and the whole set as the FinalState.
But I don't know how to achieve this function and proof.
definition initState :: "v list set\<Rightarrow> 'v list set" where
"initState vset = {x. x\<in>vset \<and> length x \<le> 1}"
This code is wrong.
First, find out what the smallest length is.
definition "minlen vset ≡ Min (length ` vset)"
Use Inf instead of Max if your set can be infinite.
Then pick out a minimum-length element using a description (# or Eps)
definition "initState vset ≡ #x. x∈vset ∧ length x = minlen vset"
I suspect there may be a better way to define your state space, however: descriptions can be tricky to work with.

Is it possible to create a "generic" function in Standard ML?

I would like to create a function remove_duplicates that takes a list of any type (e.g. can be an int list or a bool list or a int list list or a whatever list) and returns the same list without duplicates, is this possible in Standard ML?
Is a function that takes a list of any type and returns the list without duplicates possible in Standard ML?
No.
To determine if one element is a duplicate of another, their values must be comparable. "Any type", or 'a in Standard ML, is not comparable for equality. So while you cannot have a val nub : 'a list -> 'a list that removes duplicates, here are four alternative options:
What #qouify suggests, the built-in equality type ''a, so anything you can use = on:
val nub : ''a list -> ''a list
What #kopecs suggests, a function that takes an equality operator as parameter:
val nub : ('a * 'a -> bool) -> 'a list -> 'a list
Which is a generalisation of 1., since here, nub op= : ''a list -> ''a list. This solution is kind of neat since it lets you remove not only duplicates, but also redundant representatives of arbitrary equivalence classes, e.g. nub (fn (x, y) => (x mod 3) = (y mod 3)) will only preserve integers that are distinct modulo 3. But its complexity is O(n²). (-_- )ノ⌒┻━┻
Because it is O(n²), nub is considered harmful.
As the article also suggests, the alternative is to use ordering rather than equality to reduce the complexity to O(n log n). While in Haskell this means only changing the type class constraint:
nub :: Eq a => [a] -> [a]
nubOrd :: Ord a => [a] -> [a]
and adjusting the algorithm, it gets a little more complicated to express this constraint in SML. While we do have ''a to represent Eq a => a (that we can use = on our input), we don't have a similar special syntax support for elements that can be compared as less/equal/greater, and we also don't have type classes. We do have the following built-in order type:
datatype order = LESS | EQUAL | GREATER
so if you like kopecs' solution, a variation with a better running time is:
val nubOrd : ('a * 'a -> order) -> 'a list -> 'a list
since it can use something like a mathematical set of previously seen elements, implemented using some kind of balanced search tree; n inserts each of complexity O(log n) takes a total of O(n log n) steps.
One of SML's winner features is its composable module system. Instead of using parametric polymorphism and feeding the function nubOrd with an order comparison function, you can create a module that takes another module as a parameter (a functor).
First, let's define a signature for modules that represent ordering of types:
signature ORD =
sig
type t
val compare : t * t -> order
end
(Notice that there isn't a ' in front of t.)
This means that anyone could make a struct ... end : ORD by specifying a t and a corresponding compare function for ts. Many built-in types have pre-defined compare functions: int has Int.compare and real has Real.compare.
Then, define a tree-based set data structure; I've used a binary search tree, and I've skipped most functions but the ones strictly necessary to perform this feat. Ideally you might extend the interface and use a better tree type, such as a self-balancing tree. (Unfortunately, since you've tagged this Q&A both as SML/NJ and Moscow ML, I wasn't sure which module to use, since they extend the standard library in different ways when it comes to balanced trees.)
functor TreeSet (X : ORD) =
struct
type t = X.t
datatype 'a tree = Leaf | Branch of 'a tree * 'a * 'a tree
val empty = Leaf
fun member (x, Leaf) = false
| member (x, Branch (left, y, right)) =
case X.compare (x, y) of
EQUAL => true
| LESS => member (x, left)
| GREATER => member (x, right)
fun insert (x, Leaf) = Branch (Leaf, x, Leaf)
| insert (x, Branch (left, y, right)) =
case X.compare (x, y) of
EQUAL => Branch (left, y, right)
| LESS => Branch (insert (x, left), y, right)
| GREATER => Branch (left, y, insert (x, right))
end
Lastly, the ListUtils functor contains the nubOrd utility function. The functor takes a structure X : ORD just like the TreeSet functor does. It creates an XSet structure by specialising the TreeSet functor using the same ordering module. It then uses this XSet to efficiently keep a record of the elements it has seen before.
functor ListUtils (X : ORD) =
struct
structure XSet = TreeSet(X)
fun nubOrd (xs : X.t list) =
let
val init = ([], XSet.empty)
fun go (x, (ys, seen)) =
if XSet.member (x, seen)
then (ys, seen)
else (x::ys, XSet.insert (x, seen))
in rev (#1 (foldl go init xs))
end
end
Using this functor to remove duplicates in an int list:
structure IntListUtils = ListUtils(struct
type t = int
val compare = Int.compare
end)
val example = IntListUtils.nubOrd [1,1,2,1,3,1,2,1,3,3,2,1,4,3,2,1,5,4,3,2,1]
(* [1, 2, 3, 4, 5] *)
The purpose of all that mess is a nubOrd without a direct extra function parameter.
Unfortunately, in order for this to extend to int list list, you need to create the compare function for that type, since unlike Int.compare, there isn't a generic one available in the standard library either. (This is where Haskell is a lot more ergonomic.)
So you might go and write a generic, lexicographical list compare function: If you know how to compare two elements of type 'a, you know how to compare two lists of those, no matter what the element type is:
fun listCompare _ ([], []) = EQUAL (* empty lists are equal *)
| listCompare _ ([], ys) = LESS (* empty is always smaller than non-empty *)
| listCompare _ (xs, []) = GREATER (* empty is always smaller than non-empty *)
| listCompare compare (x::xs, y::ys) =
case compare (x, y) of
EQUAL => listCompare compare (xs, ys)
| LESS => LESS
| GREATER => GREATER
And now,
structure IntListListUtils = ListUtils(struct
type t = int list
val compare = listCompare Int.compare
end)
val example2 = IntListListUtils.nubOrd [[1,2,3],[1,2,3,2],[1,2,3]]
(* [[1,2,3],[1,2,3,2]] *)
So even though [1,2,3] and [1,2,3,2] contain duplicates, they are not EQUAL when you compare them. But the third element is EQUAL to the first one, and so it gets removed as a duplicate.
Some last observations:
You may consider that even though each compare is only run O(log n) times, a single compare for some complex data structure, such as a (whatever * int) list list may still be expensive. So another improvement you can make here is to cache the result of every compare output, which is actually what Haskell's nubOrdOn operator does. ┳━┳ ヽ(ಠل͜ಠ)ノ
The functor approach is used extensively in Jane Street's OCaml Base library. The quick solution was to pass around an 'a * 'a -> order function around every single time you nub something. One moral, though, is that while the module system does add verbosity, if you provide enough of this machinery in a standard library, it will become quite convenient.
If you think the improvement from O(n²) to O(n log n) is not enough, consider Fritz Henglein's Generic top-down discrimination for sorting and partitioning in linear time (2012) and Edward Kmett's Haskell discrimination package's nub for a O(n) nub.
Yes. This is possible in SML through use of parametric polymorphism. You want a function of most general type 'a list -> 'a list where 'a is a type variable (i.e., variable that ranges over types) that would be read as alpha.
For some more concrete examples of how you might apply this (the explicit type variable after fun is optional):
fun 'a id (x : 'a) : 'a = x
Here we have the identity function with type 'a -> 'a.
We can declare similar functions with some degree of specialisation of the types, for instance
fun map _ [] = []
| map f (x::xs) = f x :: map f xs
Where map has most general type ('a -> 'b) -> 'a list -> 'b list, i.e, takes two curried arguments, one with some function type and another with some list type (agrees with function's domain) and returns a new list with type given by the codomain of the function.
For your specific problem you'll probably also want to take an equality function in order to determine what is a "duplicate" or you'll probably restrict yourself to "equality types" (types that can be compared with op=, represented by type variables with two leading apostrophes, e.g., ''a).
Yes sml provides polymorphism to do such things. In many cases you actually don't care for the type of the item in your lists (or other structures). For instance this function checks (already present in the List structure) for the existence of an item in a list:
fun exists _ [] = false
| exists x (y :: l) = x = y orelse exists x l
Such function works for any type of list as long as the equal operator is defined for this type (such type is called an equality type). You can do the same for remove_duplicates. In order to work with list of items of non equality types you will have to give remove_duplicates an additional function that checks if two items are equal.

Define the notion of "pairs" using higher-order logic

Revising for a course on automated reasoning and I don't quite understand how to answer this question:
Show how the notion of pairs (x, y) can be defined in higher-order logic using a lambda abstraction. Define a function π1 that returns the first element of such a pair. Finally, show that π1(x, y) = x.
I've found similar questions on stackoverflow, but they're all to do with scheme, which I've never used. An explanation in English/relevant mathematical notation would be appreciated
Here you go
PAIR := λx. λy. λp. p x y
π1 := λp. p (λx. λy. x)
π2 := λp. p (λx. λy. y)
π1 (PAIR a b) => a
π2 (PAIR a b) => b
Check the wiki entry on Church encoding for some good examples, too
The main topic of this question is to understand how data can be represented as functions. When you're working with other paradigms , the normal way of thinking is "data = something that's stored in a variable" (could be an array, object, whatever structure you want).
But when we're in functional programming, we can also represent data as functions.
So let's say you want a function pair(x,y)
This is "pseudo" lisp language:
(function pair x y =
lambda(pick)
if pick = 1 return x
else return y )
That example, is showing a function that returns a lambda function which expects a parameter.
(function pi this-is-pair = this-is-pair 1)
this-is-pair should be constructed with a pair function, therefore, the parameter is a function which expects other parameter (pick).
And now, you can test what you need
(pi (pair x y ) ) should return x
I would highly recommend you to see this video about compound data. Most of the examples are made on lisp, but it's great to understand a concept like that.
Pairs or tuples describes Products Domain, is the union of all elements of the set A and all elements of the set B:
A × B = { (a, b) | a ∈ A, b ∈ B }
Here, A and B are diferent types, so if you for example are in a laguage program like C, Java, you can have pair like (String, Integer), (Char, Boolean), (Double, Double)
Now, the function π1, is just a function that takes a pair and returns the first element, this function is called in usually first, and that's how it looks like π1(x, y) = x, on the other hand you have second, doing the same thing but returning the second element:
fst(a, b) = a
snd(a, b) = b
When I studied the signature "Characteristics of the programming languages" in college our professor recommended this book, see the chapter Product Domain to understand well all this concepts.

What is a good way to define a finite multiplication table in Isar?

Suppose I have a binary operator f :: "sT => sT => sT". I want to define f so that it implements a 4x4 multiplication table for the Klein four group, shown here on the Wiki:
http://en.wikipedia.org/wiki/Klein_four-group
Here, all I'm attempting to do is create a table with 16 entries. First, I define four constants like this:
consts
k_1::sT
k_a::sT
k_b::sT
k_ab::sT
Then I define my function to implement the 16 entries in the table:
k_1 * k_1 = k_1
k_1 * k_a = k_a
...
k_ab * k_ab = k_1
I don't know how to do any normal-like programming in Isar, and I've seen on the Isabelle user's list where it was said that (certain) programming-like constructs have been intentionally de-emphasized in the language.
The other day, I was trying to create a simple, contrived function, and after finding the use of if, then, else in a source file, I couldn't find a reference to those commands in isar-ref.pdf.
In looking at the tutorials, I see definition for defining functions in a straightforward way, and other than that, I only see information on recursive and inductive functions, which require datatype, and my situation is more simple than that.
If left to my own devices, I guess I would try and define a datatype for those 4 constants shown above, and then create some conversion functions so that I end up with a binary operator f :: sT => sT => sT. I messed around a little with trying to use fun, but it wasn't turning out to be a simple deal.
I had done a little experimenting with fun and inductive
UPDATE: I add some material here in response to the comment telling me that Programming and Proving is where I'll find the answers. It seems I might be going astray of the ideal Stackoverflow format.
I had done some basic experimenting, mainly with fun, but also with inductive. I gave up on inductive fairly fast. Here's the type of error I got from simple examples:
consts
k1::sT
inductive k4gI :: "sT => sT => sT" where
"k4gI k1 k1 = k1"
--"OUTPUT ERROR:"
--{*Proofs for inductive predicate(s) "k4gI"
Ill-formed introduction rule ""
((k4gI k1 k1) = k1)
Conclusion of introduction rule must be an inductive predicate
*}
My multiplication table isn't inductive, so I didn't see that inductive was what I should spend my time chasing.
"Pattern matching" seems a key idea here, so I experimented with fun. Here's some really messed up code trying to use fun with only a standard function type:
consts
k1::sT
fun k4gF :: "sT => sT => sT" where
"k4gF k1 k1 = k1"
--"OUTPUT ERROR:"
--"Malformed definition:
Non-constructor pattern not allowed in sequential mode.
((k4gF k1 k1) = k1)"
I got that kind of error, and I had read things like this in Programming and Proving:
"Recursive functions are defined with fun by pattern matching over datatype constructors.
That all gives a novice the impression that fun requires datatype. As far its big brother function, I don't know about that.
It seems here, all I need is a recursive function with 16 base cases, and that would define my multiplication table.
Is function the answer?
In editing this question, I remembered function from the past, and here's function at work:
consts
k1::sT
function k4gF :: "sT => sT => sT" where
"k4gF k1 k1 = k1"
try
The output of try is telling me it can be proved (Update: I think it's actually telling me that only 1 of the proof steps can be prove.):
Trying "solve_direct", "quickcheck", "try0", "sledgehammer", and "nitpick"...
Timestamp: 00:47:27.
solve_direct: (((k1, k1) = (k1, k1)) ⟹ (k1 = k1)) can be solved directly with
HOL.arg_cong: ((?x = ?y) ⟹ ((?f ?x) = (?f ?y))) [name "HOL.arg_cong", kind "lemma"]
HOL.refl: (?t = ?t) [name "HOL.refl"]
MFZ.HOL⇣'eq⇣'is⇣'reflexive: (?r = ?r) [name "MFZ.HOL⇣'eq⇣'is⇣'reflexive", kind "theorem"]
MFZ.HOL_eq_is_reflexive: (?r = ?r) [name "MFZ.HOL_eq_is_reflexive", kind "lemma"]
Product_Type.Pair_inject:
(⟦((?a, ?b) = (?a', ?b')); (⟦(?a = ?a'); (?b = ?b')⟧ ⟹ ?R)⟧ ⟹ ?R)
[name "Product_Type.Pair_inject", kind "lemma"]
I don't know what that means. I only know about function because of trying to prove an inconsistency. I only know it doesn't complain as much. If using function like this is how I define my multiplication table, then I'm happy.
Still, being an argumentative type, I didn't learn about function in a tutorial. I learned about it several months ago in a reference manual, and I still don't know much about how to use it.
I have a function which I prove with auto, but the function is probably no good, fortunately. That adds to the function's mystery. There's information on function in Defining Recursive Functions in Isabelle/HOL, and it compares fun and function.
However, I haven't seen one example of fun or function that doesn't use a recursive datatype, such as nat or 'a list. Maybe I didn't look hard enough.
Sorry for being verbose and this not ending up as a direct question, but there's no tutorial with Isabelle that takes a person directly from A to B.
Below, I don't adhere to an "only answer the question" format, but I am responding to my own question, and so everything I say will be of interest to the original poster.
(2nd update begin)
This should be my last update. To be content with "unsophisticated methods", it helps to be able to make comparisons to see the "low tech" way can be the best way.
I finally quit trying to make my main type work with the new type, and I just made me a Klein four-group out of a datatype like this, where the proof of associativity is at the end:
datatype AT4k = e4kt | a4kt | b4kt | c4kt
fun AOP4k :: "AT4k => AT4k => AT4k" where
"AOP4k e4kt y = y"
| "AOP4k x e4kt = x"
| "AOP4k a4kt a4kt = e4kt"
| "AOP4k a4kt b4kt = c4kt"
| "AOP4k a4kt c4kt = b4kt"
| "AOP4k b4kt a4kt = c4kt"
| "AOP4k b4kt b4kt = e4kt"
| "AOP4k b4kt c4kt = a4kt"
| "AOP4k c4kt a4kt = b4kt"
| "AOP4k c4kt b4kt = a4kt"
| "AOP4k c4kt c4kt = e4kt"
notation
AOP4k ("AOP4k") and
AOP4k (infixl "*" 70)
theorem k4o_assoc2:
"(x * y) * z = x * (y * z)"
by(smt AOP4k.simps(1) AOP4k.simps(10) AOP4k.simps(11) AOP4k.simps(12)
AOP4k.simps(13) AOP4k.simps(2) AOP4k.simps(3) AOP4k.simps(4) AOP4k.simps(5)
AOP4k.simps(6) AOP4k.simps(7) AOP4k.simps(8) AOP4k.simps(9) AT4k.exhaust)
The consequence is that I am now content with my if-then-else multiplication function. Why? Because the if-then-else function is very conducive to simp magic. This pattern matching doesn't work any magic in and of itself, not to mention that I would still have to work out the coercive subtyping part of it.
Here's the if-then-else function for the 4x4 multiplication table:
definition AO4k :: "sT => sT => sT" where
"AO4k x y =
(if x = e4k then y else
(if y = e4k then x else
(if x = y then e4k else
(if x = a4k y = c4k then b4k else
(if x = b4k y = c4k then a4k else
(if x = c4k y = a4k then b4k else
(if x = c4k y = b4k then a4k else
c4k)))))))"
Because of the one nested if-then-else statement, when I run auto, it produces 64 goals. I made 16 simp rules, one for every value in the multiplication table, so when I run auto, with all the other simp rules, the auto proof takes about 90ms.
Low tech is the way to go sometimes; it's a RISC vs. CISC thing, somewhat.
A small thing like a multiplication table can be important for testing things, but it can't be useful if it's gonna slow my THY down because it's in some big loop that takes forever to finish.
(2nd update end)
(Update begin)
(UPDATE: My question above falls under the category "How do I do basic programming in Isabelle, like with other programming languages?" Here, I go beyond the specific question some, but I try to keep my comments about the challenge to a beginner who is trying to learn Isabelle when the docs are at the intermediate level, at least, in my opinion they are.
Specific to my question, though, is that I have need for a case statement, which is a very basic feature of many, many programming languages.
In looking for a case statement today, I thought I hit gold after doing one more search in the docs, this time in Isabelle - A Proof Assistant for
Higher-Order Logic.
On page 5 it documents a case statement, but on page 18, it clarifies that it's only good for datatype, and I seem to confirm that with an error like this:
definition k4oC :: "kT => kT => kT" (infixl "**" 70) where
"k4oC x y = (case x y of k1 k1 => k1)"
--{*Error in case expression:
Not a datatype constructor: "i130429a.k1"
In clause
((k1 k1) ⇒ k1)*}
This is an example that a person, whether expert or beginner, has a need for a tutorial to run through the basic programming features of Isabelle.
If you say, "There are tutorials that do that." I say, "No, there aren't, not in my opinion".
The tutorials emphasize the important, sophisticated features of Isabelle that separate it from the crowd.
That's commentary, but it's commentary meant to tie into the question, "How do I learn Isabelle?", and which my original question above is related to.
The way you learn Isabelle without being a PhD graduate student at Cambridge, TUM, or NICTA, is you struggle for 6 to 12 months or more. If during that time you don't abandon, you can be at a level that will allow you to appreciate the intermediate level instruction available. Experience may vary.
For me, the 3 books that will take me to the next level of proving, weaning me off of auto and metis, when I find time to go through them, are
Isabelle - A Proof Assistant for Higher-Order Logic
Programming and Proving in Isabelle/HOL
Isabelle/Isar --- a versatile environment for human-readable formal proof documents
If someone says, "You've abused the Stackoverflow answer format by engaging in long-winded commentary and opinion."
I say, "Well, I asked for a good way to do some basic programming in Isabelle, where I was hoping for something more sophisticated than a big if-then-else statement. No one provided anything close to what I asked for. In fact, I am who provided a pattern matching function, and what I needed to do it is not even close to being documented. Pattern matching is a simple concept, but not necessarily in Isabelle, due to the proof requirements for recursive functions. (If there's a simple way to do it to replace my if-then-else function below, or even a case statement way, I'd sure like to know.)
Having said that, I am inclined to take some liberties, and there are, at this time, only 36 views for this page anyway, of which probably, at least 10 come from my browser.
Isabelle/HOL is a powerful language. I'm not complaining. It only sounds like it.)
(Update end)
It can count for a lot just to know that something is true or false, in this case being told that function can work with non-inductive types. However, how I end up using function below is not a result of anything I've seen in any one Isabelle document, and I had need for this former SO question on coercive subtyping:
What is an Isabelle/HOL subtype? What Isar commands produce subtypes?
I end up with two ways that I completed a 2x2 part of my multiplication table. I link here to the theory: as ASCII friendly A_i130429a.thy, jEdit friendly i130429a.thy, the PDF, and folder.
The two ways are:
The clumsy but fast and simp friendly if-then-else way. The definition takes 0ms, and the proof takes 155ms.
The pattern matching way using function. Here I could think aloud in public for a long time about this way of doing things, but I won't. I know I'll use what I've learned here, but it's definitely not an elegant solution for a simple multiplication table function, and it's far from obvious that a person would have to do all that to create a basic function that uses pattern matching. Of course, maybe I don't have to do all that. The definition takes 391ms, and the proof takes 317ms.
As to having to resort to using if-then-else, either Isabelle/HOL is not feature rich when it comes to basic programming statements, or these basic statements aren't documented. The if-then-else statement is not even in the Isar Reference Manual index. I think, "If it's not documented, maybe there's a nice, undocumented case statement like Haskell has". Still, I'd take Isabelle over Haskell any day.
Below, I explain the different sections of A_i130429a.thy. It's sort of trivial, but not completely, since I haven't seen an example to teach me how to do that.
I start with a type and four constants, which remain undefined.
typedecl kT
consts
k1::kT
ka::kT
kb::kT
kab::kT
Of note is that the constants remain undefined. That I'm leaving a lot of things undefined is part of why I have problems finding good examples in docs and sources to use as templates for myself.
I do a test to try and intelligently use function on a non-inductive datatype, but it doesn't work. With my if-then-else function, after I figure out I'm not restricting my function domain, I then see that the problem with this function was also with the domain. The function k4f0 is wanting x to be k1 or ka for every x, which obviously is not true.
function k4f0 :: "kT => kT" where
"k4f0 k1 = k1"
| "k4f0 ka = ka"
apply(auto)
apply(atomize_elim)
--"goal (1 subgoal):
1. (!! (x::sT). ((x = k1) | (x = ka)))"
I give up and define me an ugly function with if-then-else.
definition k4o :: "kT => kT => kT" (infixl "**" 70) where
"k4o x y =
(if x = k1 & y = k1 then k1 else
(if x = k1 & y = ka then ka else
(if x = ka & y = k1 then ka else
(if x = ka & y = ka then k1 else (k1)
))))"
declare k4o_def [simp add]
The hard part becomes trying to prove associativity of my function k4o. But that's only because I'm not restricting the domain. I put in an implication into the statement, and the auto magic kicks in, the fastforce magic is there also, and faster, so I use it.
abbreviation k4g :: "kT set" where
"k4g == {k1, ka}"
theorem
"(x \<in> k4g & y \<in> k4g & z \<in> k4g) --> (x ** y) ** z = x ** (y ** z)"
by(fastforce)(*155ms*)
The magic makes me happy, and I'm then motivated to try and get it done with function and pattern matching. Because of the recent SO answer on coercive subtyping, linked to above, I figure out how to fix the domain with typedef. I don't thinks it's the perfect solution, but I definitely learned something.
typedef kTD = "{x::kT. x = k1 | x = ka}"
by(auto)
declare [[coercion_enabled]]
declare [[coercion Abs_kTD]]
function k4f :: "kTD => kTD => kT" (infixl "***" 70) where
"k4f k1 k1 = k1"
| "k4f k1 ka = ka"
| "k4f ka k1 = ka"
| "k4f ka ka = k1"
by((auto),(*391ms*)
(atomize_elim),
(metis (lifting, full_types) Abs_kTD_cases mem_Collect_eq),
(metis (lifting, full_types) Rep_kTD_cases Rep_kTD_inverse mem_Collect_eq),
(metis (lifting, full_types) Rep_kTD_cases Rep_kTD_inverse mem_Collect_eq),
(metis (lifting, full_types) Rep_kTD_cases Rep_kTD_inverse mem_Collect_eq),
(metis (lifting, full_types) Rep_kTD_cases Rep_kTD_inverse mem_Collect_eq))
termination
by(metis "termination" wf_measure)
theorem
"(x *** y) *** z = x *** (y *** z)"
by(smt
Abs_kTD_cases
k4f.simps(1)
k4f.simps(2)
k4f.simps(3)
k4f.simps(4)
mem_Collect_eq)(*317ms*)
A more or less convenient syntax for defining a "finite" function is the function update syntax: For a function f, f(x := y) represents the function %z. if z = x then y else f z. If you want to update more than one value, separate them with commas: f(x1 := y1, x2 := y2).
So, for example function which is addition for 0, 1 and undefined else could be written as:
undefined (0 := undefined(0 := 0, 1 := 1),
1 := undefined(0 := 1, 1 := 2))
Another possibility to define a finite function is to generate it from a list of pairs; for example with map_of. With f xs y z = the (map_of xs (y,z)), then the above function could be written as
f [((0,0),0), ((0,1),1), ((1,0),1), ((1,1),1)]
(Actually, it is not quite the same function, as it might behave differently outside the defined Domain).

Canonical way to get a more specific lemma

Say I have a lemma mylem: foo ?a = bar ?a, and I need to apply it on a goal that has two occurrences of foo, e.g. baz (foo (f p q)) (foo (g r s)), but only at one of these positions. I know of two ways of doing that without having to write out all of p,q..., which can be complex expressions.
Using apply (subst mylem) followed by an appropriate number (here, zero or one) of back commands.
Using apply (subst mylem[where a = 'foo x y', standard]), where x and y are unbound names.
The use of subst here is just for demonstration; I really do want to modify the lemma, e.g. to use it with rule when there are multiple possible matches that I’d like to disambiguate this way.
Both approaches look like bad style to me. Is there a nicer way of achieving that?
You can tell subst which occurrence it should replace: subst (i) mylem unfolds mylem at the i-th matching occurrence. This saves you the back steps. You can also list multiple positions as in subst (1 2) mylem. If you want to unfold mylem in premises, use subst (asm) (1 2) mylem.
In general, I do not know a way to achieve what you want inside an apply script. On the theory level, you can use lemmas with the for clause to generalise over locally introduced variables:
lemmas mylem' = mylem[where a="f x y"] for x y
Inside a structured proof, you can do it explicitly like this:
{ fix x y note mylem[where a="f x y"] }
note mylem' = this

Resources