I know I am missing something obvious, but I am lost in how to make this one work. I am trying to write the set comprehension for a kind of "Cartesian product". Given a finite set S of finite sets, I would like to
build a sets of sets of pairs, where each pair is given by one of the sets in S and one value v in that set; and each set has exactly one pair for each of set sets in S.
Basically, given the (simplistic) example
definition s1 :: "nat set" where
"s1 = {1, 2}"
definition s2 :: "nat set" where
"s2 = {4, 5}"
definition S :: "nat set set" where
"S = {s1, s2}"
value "{{(s, v) | s. s ∈ S} | v. v ∈ s}"
I would like the evaluation to become {{({1, 2}, 1), ({4, 5}, 4)}, {({1, 2}, 1), ({4, 5}, 5)}, {({1, 2}, 2), ({4, 5}, 4)}, {({1, 2}, 2), ({4, 5}, 5)}}
Instead, it does evaluate to (what it is supposed to, I realize that)
"(λu. {({1, 2}, u), ({4, 5}, u)}) ` s"
:: "(nat set × 'a) set set"
The u variable is bound to a free variable "s", I don't know how to have different "u"s and bound each u to its "associated"
s ∈ S
Thank you for any points or directions.
The simple way is to avoid the problem and express everything as image over the set:
value "(λv. (Pair v) ` S) ` S"
In general, there are two ways to do this kind of things: make the code generator work or the better way, namely adapt the definition to make them easier to fit the evaluation model.
Related
So far I can define a map
map_of [(1, 2), (3, 4::int)]
of type
'a => int option
When I try to get the domain of the map:
dom (map_of [(1, 2), (3, 4::int)])
give the error
Wellsortedness error:
Type 'b not of sort enum
Cannot derive subsort relation {equal,numeral} < enum
The examples in Enum.thy only show finite cases, how do you prove the enum property for an infinite type like int or nat?
Update 1:
Fixed the syntax and give the exact error message
This looks like a problem that occurs when trying to evaluate the expression
dom (map_of [(1, 2), (3, 4::int)])
e.g. using the "value" command.
The reason why this doesn't work is that "map_of" essentially gives you a function and the domain of a function is not in general computable.
Still, you can use your map and carry out proofs:
lemma "dom (map_of [(1, 2), (3, 4::nat)]) = {1, 3}"
by simp
Or, if you'd rather have something computable, then you can just keep using lists of tuples. In Isabelle2016-1, there will be a dedicated "finite map" type too:
theory Scratch
imports "~~/src/HOL/Library/Finite_Map"
begin
value "fmdom' (fmap_of_list [(1, 2), (3, 4::nat)])"
(* prints "{1, 1 + 1 + 1}" *)
Again a small example with unexpected results.
theory Scratch
imports Main
begin
datatype test = aa | bb | plus test test
axiomatization where
testIdemo : "x == plus x x"
lemma test1 : "y == plus y y"
Now i get the following messages:
Auto solve_direct: The current goal can be solved directly with
Scratch.testIdemo: ?x ≡ test.plus ?x ?x
Auto Quickcheck found a counterexample:
y = aa
Evaluated terms:
test.plus y y = test.plus aa aa
and when i try to run sledgehammer i get:
"remote_vampire": Try this: using testIdemo by auto (0.0 ms).
"spass": The prover derived "False" from "test.distinct(5)" and "testIdemo".
This could be due to inconsistent axioms (including "sorry"s) or to a bug in Sledgehammer.
If the problem persists, please contact the Isabelle developers.
Is this due to me messing with ==?
Or do i need to set some other sort of restriction for my axioms?
Follow up:
Apparently i should not play with equals :P
So i need to define my own relation.
axiomatization
testEQ :: "test ⇒ test ⇒ bool" (infixl "=" 1)
where
reflexive [intro]: "x = x" and
substitution [elim]: "x = y ⟹ B x = B y" and
symmetric : "x = y ⟹ y = x"
So i guess i have to define my basic properties. reflexiveness, substitution and symmetry seem nice for a start. I could make it generic with 'a => 'a => bool
now i would go on to define more of my relation. to stay with the example:
axiomatization
plus :: "test⇒ test⇒ test" (infixl "+" 35)
where
commutative: "x + y = y + x" and
idemo: "x + x = x"
a) Is this so far correct
b) How to proceed from here
So far i don't think this would replace subterms out of lemmas which is kinda the point of equivalence.
Your axiom implies e.g. aa = plus aa aa, which is false, because constructors of a datatype are always distinct, by construction. (cf. thm test.distinct)
In fact, if you use axiomatization, you should really know what you're doing – it's very easy to introduce inconsistencies that way. (obviously)
If you want to have a type with certain properties, you have to construct it. For instance, you could define a representation type of your type (e.g. as a datatype), then define some equality relation on it (i.e. what values should be equal to what other values) and then define the ‘real’ type as the quotient type of your representation type over that relation.
Update 2 (151015)
I put some source below. It shows a skeleton of what I may use.
With some help, I'm getting more sophisticated. I now know the difference between a numeral type type, and a constant of type numeral. The notation is all the same, and I'm used to using functions that operate on terms, not types. With CARD, that changes the paradigm some.
As far as numeral type notation, even with show_consts, it's not obvious that I'm looking at a type or a term. So, I draw off always using show_sorts and show_consts. A 0-ary type constructor type, like nat, never gets annotated with anything. Knowing that helps.
I said that a certain theorem wasn't being proved by magic without not importing Numeral_Type, but that's not true.
Succinct syntax is important, so getting good type inference is important. It looks like I get good type inference when using the numeral type.
From the answer, I also got my first use of using a dummy type, which, at this point, appears to be a much better way to do things.
Here's some source:
theory i151013c_numeral_has_type_enforcement
imports Complex_Main "~~/src/HOL/Library/Numeral_Type"
begin
declare [[show_sorts, show_consts]]
datatype ('a,'size) vD = vC "'a list"
definition CARD_foo :: "('a,'s::{finite,card_UNIV}) vD => ('a,'s) vD => nat"
where
"CARD_foo x y = card (UNIV :: 's set)"
notation (input) vC ("vC|_" [1000] 1000)
notation (output) vC ("vC|_" [1000] 999)
value "CARD_foo (vC|x::(nat,32) vD) vC|y = (32::nat)" (*True*)
value "CARD_foo (vC|x::(nat,65536) vD) vC|y = 65536" (*True*)
type_synonym nv3 = "(nat, 3) vD"
notation CARD_foo (infixl "*+*" 65)
value "vC|m *+* (vC|n::nv3) = 3" (*True*)
type_notation (input) vD ("< _ , _ >")
term "x::<'a,'s::{finite,card_UNIV}>"
term "vC|m *+* (vC|n::<'a,64>) = 64"
value "vC|m *+* (vC|n::<'a,64>) = 64" (*True*)
(*Next, Am I adding 2 types of type numeral? Or am I adding 2 constants of
type numeral, which produces a numeral type? The notation for numeral types
and numeral type constants is identical.*)
value "vC|[3] *+* (vC|y::<nat,2 + 3>)"
term "vC|[3] *+* (vC|y::<nat,2 + 3>)"
(*I guess 2 and 3 are types. In the output panel, 0-ary types, such as 'nat',
don't get annotated with anything, where nat constants do.*)
lemma "vC|[3] *+* (vC|y::<nat,2 + 3>) = 5"
by(simp add: CARD_foo_def)
(*Type error clash. Oh well. Just don't do that.*)
term "(vC|x::<'a,5>) *+* (vC|y::<'a,2 + 3>)"
definition vCARD :: "('a, 's::{finite,card_UNIV}) vD => nat" where
"vCARD x = CARD('s)"
declare vCARD_def [simp add]
lemma
"vCARD(x::<'a,'s::{finite,card_UNIV}>) =
vCARD(y::<'b,'s::{finite,card_UNIV}>)"
by(simp)
end
Update (151014)
Here I explain to M.Eberl why I don't use the numeral type, based upon what I know and have experienced.
Related comment about typedef and a past question
A while back, I got exposed to ~~/src/HOL/Library/Cardinality, along with Numeral_Type from this answer by M.Eberl:
Trying to generalize a bit vector that uses typedef, bool list, and nat length
That question was also related to typedef. Partly from my experiments at that time, I try to stay away from typedef, and use the magic that comes with datatype.
Even today, I started having problems with the sz8 typedef not working with value, because of abstraction problems. After looking back at the answer linked to above, it partially shows what has to be done to get typedef working with value. I have a size8 in the new source I include that shows what I did. I think there's a problem with the equal function, that it needs to be fixed similar to what's shown in the answer above.
An Example size datatype and vector datatype
Now, I make a few comments about the two example datatypes in the second source I include below.
The use case for a size type is for vector length, where the vectors are lists.
The size type enforces that for a binary operation, two vectors have the same length. I then only have to check that the two vectors actually are the right length.
The problem with the numeral type is that there's no type enforcement. My example function has the following signature, with the datatype shown:
datatype ('a,'size) vD = vC "'a list" 'size
CARD_foo :: "(nat,'s::card_UNIV) vD => (nat,'s) vD => nat"
But I can to this:
term "CARD_foo (vC [] 10) (vC [] 11)"
Other comments are in the source. I have a typedef size8 at the end, and I don't know, at this time, how to fix that.
Though there's no type enforcement with the numeral type, I guess I can depend on type size using CARD, based on this:
theorem CARD_of_type_of_terms_of_same_type_are_equal:
"CARD_foo (vC n size1term) = CARD_foo (vC m size2term)"
unfolding CARD_foo_def
by(auto simp add: CARD_foo_def)
To get that by magic, I had to not import "~~/src/HOL/Library/Numeral_Type".
Thanks for the help. It's invaluable, and thanks for how to get the proofs I asked for originally. It helps to learn things here and there about typedef.
The new example source:
theory i151013b_2nd
imports Complex_Main (*"$GEZ/e/IsE"*)
"~~/src/HOL/Library/Cardinality" "~~/src/HOL/Library/Numeral_Type"
begin
declare [[show_sorts, show_consts]]
(*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*)
(*::¦ NUMERAL TYPE DOESN'T GUARANTEE TYPE ENFORCEMENT FOR A BINARY OP ¦:::*)
(*----------------------------*)
(*The size type as a datatype.*)
datatype sz8D = sz8C bool bool bool
(*-----------------------------------*)
(*Typdef 'n <= 7 would be preferable.*)
lemma UNIV_sz8D:
"UNIV =
{sz8C False False False, sz8C False False True, sz8C False True False,
sz8C False True True, sz8C True False False, sz8C True False True,
sz8C True True False, sz8C True True True}"
by(auto, metis (full_types) sz8D.exhaust)
lemma card_UNIV_sz8D [simp]: "card (UNIV :: sz8D set) = 8"
by(unfold UNIV_sz8D, auto)
instantiation sz8D :: card_UNIV
begin
definition "finite_UNIV = Phantom(sz8D) True"
definition "card_UNIV = Phantom(sz8D) 8"
instance
apply(default)
unfolding UNIV_sz8D finite_UNIV_sz8D_def card_UNIV_sz8D_def
by(auto)
end
(*-----------------------------------------*)
(*The vector type with an example function.*)
datatype ('a,'size) vD = vC "'a list" 'size
definition CARD_foo :: "(nat,'s::card_UNIV) vD => (nat,'s) vD => nat" where
"CARD_foo x y = card (UNIV :: 's set)"
thm CARD_foo_def
(*--------------------------------------------------------*)
(*sz8D: Size enforcement. Error if I mix other size types.*)
value "CARD_foo (vC [] (s::sz8D)) (vC [1] (t::sz8D))" (*outputs 8*)
value "CARD_foo (vC [] (sz8C False False False)) (vC [1] (t::sz8D))"
(*-------------------------------------*)
(*numeral: No enforcement of size type.*)
term "CARD_foo (vC [] 10) (vC [] 11)" (*
"CARD_foo (vC [] (10::'a::{card_UNIV,numeral}))
(vC [] (11::'a::{card_UNIV,numeral}))" :: "nat" *)
(*Can't eval the type to nat, even if they're the same*)
value "CARD_foo (vC [] 10) (vC [] 10)"
(*But here, CARDs are different anyway; no enforcement, no value.*)
value "CARD_foo (vC [] 10) (vC [] 11)" (*
"of_phantom card_UNIV_class.card_UNIV" :: "nat"*)
lemma "CARD_foo (vC [] 10) (vC [] 11) = z" oops (*
show_consts:
CARD_foo (vC [] (10::'a)) (vC [] (11::'a)) = z
'a :: {card_UNIV,numeral} *)
(*Can evaluate when there's not a conflict.*)
term "CARD(10)"
value "CARD(10)" (*outputs 10*)
lemma "CARD(10) = 10" by simp (*show_consts: 'UNIV :: 10 set'*)
value "CARD(11)" (*outputs 11*)
(*No eval if CARD('a) is used in a function.*)
definition fooID :: "'a::card_UNIV => nat" where
"fooID x = CARD('a)"
term "fooID(5)"
value "fooID(5)"
(*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*)
(*::¦ HOWEVER, BY FUNCTION UNIQUENESS, I SUPPOSE THERE'S NO AMBIGUITY ¦:::*)
(*[>) I have to drop down to only 'src/HOL/Library/Cardinality' to get this.
[>) For some reason, it won't unfold the definition.*)
theorem CARD_of_type_of_terms_of_same_type_are_equal:
"CARD_foo (vC n size_1term) = CARD_foo (vC m size_2term)"
unfolding CARD_foo_def
by(auto simp add: CARD_foo_def)
(*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*)
(*::¦ CAN'T USE TYPEDEF AFTERALL IF I CAN'T FIX THIS ¦::::::::::::::::::::*)
(*NOTE ABOUT PLUG'N'PLAY:
[>) See http://stackoverflow.com/q/27415275
[>) 'value' for 'CARD_foo' needs class 'equal'
[>) '[code abstract]' didn't work, so I used '[code]'.*)
typedef size8 = "{n::nat. n ≤ 7}"
morphisms size8_to_nat Abs_size8
by(blast)
definition nat_to_size8 :: "nat => size8" where
"nat_to_size8 n == if n ≤ 7 then Abs_size8 n else Abs_size8 0"
lemma nat_to_size8_code [code]:
"size8_to_nat (nat_to_size8 n) = (if n ≤ 7 then n else 0)"
unfolding nat_to_size8_def
by(simp add: Abs_size8_inverse)
setup_lifting type_definition_size8
instantiation size8 :: equal
begin
lift_definition equal_size8 :: "size8 => size8 => bool" is "λx y. x = y" .
instance
by(default, transfer, auto simp add: equal_size8_def)
end
instantiation size8 :: card_UNIV
begin
definition "finite_UNIV = Phantom(size8) True"
definition "card_UNIV = Phantom(size8) 8"
instance sorry
end
value "CARD_foo (vC [] (Abs_size8 0)) (vC [] (Abs_size8 0))" (*
Abstraction violation: constant Abs_size8 *)
end
Original Question
I'm using typedef to define some types that are used as size types, to be used like this: CARD(sz8). I can use datatype, but it takes a lot longer for it to set itself up.
I guess I don't understand how to show two values are unique with the inverse theorems generated by typedef for sz8.
I have my type, sz8, and I instantiate it as card_UNIV. What's incomplete is my theorem card_UNIV_sz8, which is "card (UNIV::sz8 set) = 8".
theory i151013a
imports Complex_Main "~~/src/HOL/Library/Cardinality"
"$GEZ/e/IsE"
begin
declare [[show_sorts, show_consts]]
typedef sz8 = "{n::nat. n ≤ 7}"
by(blast)
theorem UNIV_sz8:
"UNIV = {s::sz8. ∃n. n ≤ 7 ∧ s = Abs_sz8 n}"
using Rep_sz8 Rep_sz8_inverse
by(fastforce)
theorem foo1:
assumes "Abs_sz8 n ∈ {s::sz8. ∃n ≤ 7. s = Abs_sz8 n}"
shows "n ∈ {n. n ≤ 7}"
proof
fix n :: nat
note assms
obtain n1 where
f1: "n1 ≤ (7::nat) ∧ Abs_sz8 n = Abs_sz8 n1"
using Rep_sz8 Rep_sz8_inverse
by(fastforce)
hence "n = n1"
oops
find_theorems name: "sz8"
instance sz8 :: finite
apply default
unfolding UNIV_sz8
by(simp)
theorem card_UNIV_sz8 [simp]:
"card (UNIV::sz8 set) = 8"
unfolding UNIV_sz8
sorry
instantiation sz8 :: card_UNIV
begin
definition "finite_UNIV = Phantom(sz8) True"
definition "card_UNIV = Phantom(sz8) 8"
instance
apply default
unfolding finite_UNIV_sz8_def card_UNIV_sz8_def
by(simp_all)
end
end
The answer to your question
First of all: I will answer your question, but then I will tell you why what you are doing is unnecessary.
You can show the distinctness of the values using the theorem sz8.Abs_sz8_inject, which shows up if you do find_theorems Abs_sz8:
(?x::nat) ∈ {n::nat. n ≤ (7::nat)} ⟹
(?y::nat) ∈ {n::nat. n ≤ (7::nat)} ⟹
(Abs_sz8 ?x = Abs_sz8 ?y) = (?x = ?y)
You can prove your theorem e.g. like this:
lemma sz8_image: "x ∈ Abs_sz8 ` {0..7}"
by (cases x rule: sz8.Abs_sz8_cases) auto
theorem card_UNIV_sz8 [simp]: "card (UNIV::sz8 set) = 8"
proof -
from sz8_image have "UNIV = Abs_sz8 ` {0..7}" by blast
also from sz8.Abs_sz8_inject have "card … = card {0..(7::nat)}"
by (intro card_image inj_onI) simp_all
finally show ?thesis by simp
qed
What you should do instead
Have a look at the theory ~~/src/HOL/Library/Numeral_Type, where ~~ stands for the Isabelle root directory.
This defines a type n for every positive integer n, which contains exactly the numbers from 0 to n - 1 and even defines lots of typeclass instances and modular arithmetic on them. For example:
value "(2 - 5 :: 10) = 7"
> "True" :: "bool"
This is probably exactly what you want and it comes fully set up; doing all of this by hand is quite tedious, and if you ever need a size 16 type, you have to do the same thing all over again.
Update: More on numeral types
In your updated question, you claim that type checking for numeral types does not work. That is not correct; the problem is merely that the 10 in your vC [] 10 has no meaning. Your intention was probably to specify that the length parameter 'size in the type of that function must be 10.
However, every numeral type contains a 10. For instance, (10 :: 5) = 0 and (10 :: 6) = 4. Therefore, the 10 and 11 in there do not cause any type restrictions at all.
What you have to do is constrain 'size at the type level:
datatype ('a,'size) vD = vC "'a list"
consts CARD_foo :: "(nat,'s::card_UNIV) vD => (nat,'s) vD => nat"
term "CARD_foo (vC [] :: (nat, 10) vD) (vC [] :: (nat, 11) vD)"
(* Type error *)
If you really want to do something on the value-level similar to what you tried to do, you can use the following trick:
datatype ('a,'size) vD = vC "'a list" "'size itself"
consts CARD_foo :: "(nat,'s::card_UNIV) vD => (nat,'s) vD => nat"
term "CARD_foo (vC [] TYPE(10)) (vC [] TYPE(11))"
'a itself is basically a singleton type that contains the value TYPE('a). I think the variant without these itself values is probably more convenient in the long run though.
As for why your CARD_of_type_of_terms_of_same_type_are_equal does not work, I cannot say without seeing the definition of the constants involved, I am quite sure that everything that works with your hand-crafted sz8 type will work with numeral types.
At the end of the day, you can always replace sz8 everywhere in your code with 8 and everything should still work.
I was looking at the case operator, to see what it can do for me.
There's not a problem. Given the examples I'm working with, I accept that "it does what it does", but I ask some questions in case there's something more to learn.
It appears that the case operator can't take arguments that are constants unless they're datatype constants. If they aren't, it gives the uninformative message: "Error in case expression: type mismatch".
Can I get case to pattern match on a non-datatype constant?
The keyword simps_of_case sometimes produces simp rules from a case, and sometimes it doesn't.
Is there something I should know about the example below where it just reproduces the yield2_def as the simp rule?
I took one example for simps_of_case from How to define a partial function in Isabelle?. It seems that I learned somewhere that case is designed around datatype, but I don't find where I learned about that.
I include a short theory with the examples:
theory i150903a__a0
imports Complex_Main "~~/src/HOL/Library/Simps_Case_Conv"
begin
(*************************************************************************)
section{* simps_of_case: Doesn't generate any new simps *}
(*(58)/src/HOL/Lazy_Sequence.thy
[∙) Doesn't generate any new simp rules. Because of 'list_of_lazy_sequence'?*)
definition yield2 :: "'a lazy_sequence => ('a × 'a lazy_sequence) option"
where
"yield2 xq = (case list_of_lazy_sequence xq of
[] => None
| x # xs => Some (x, lazy_sequence_of_list xs))"
thm yield2_def
find_theorems name: yield2
simps_of_case yield2_simps[simp]: yield2_def
thm yield2_simps
find_theorems name: yield2
(*************************************************************************)
section{* simps_of_case: Does generate new simps *}
(*140813a__SOz__How to define a partial function in Isabelle*)
partial_function (tailrec) oddity :: "nat => nat" where
"oddity x = (case x of (Suc (Suc n)) => n | 0 => 0 )"
thm oddity.simps
find_theorems name: oddity
simps_of_case oddity_simps[simp]: oddity.simps
thm oddity_simps
find_theorems name: oddity
(*************************************************************************)
section{* Case constant arguments must be datatypes? *}
declare[[show_sorts]]
(*Works*)
term "case (x,y) of (None, None) => (0::'a::zero, 0::'b::zero)"
term "case (x,y) of (0::nat, 0::nat) => (0::'a::zero, 0::'b::zero)"
term "case (x,y) of (0::nat, x # xs) => (0::'a::zero, 0::'b::zero)"
term "case (x,y) of (a,b) => (0::'a::zero, 0::'b::zero)"
fun foofun :: "('a::zero, 'b::zero) prod => ('a, 'b) prod" where
"foofun (x,y) = (case (x,y) of (a,b) => (0,0))"
(*OUTPUT: "Error in case expression: type mismatch"*)
term "case (x,y) of (0::nat, 0::int) => (0::'a::zero, 0::'b::zero)"
fun foofun :: "('a::zero, 'b::zero) prod => ('a, 'b) prod" where
"foofun (x,y) = (case (x,y) of (0,0) => (0,0))"
(*************************************************************************)
section{* Theory end *}
end
Case syntax is only sugar – it gets desugared to a nested application of the appropriate "case combinators". The datatype manual hints at that briefly in §2.3. As far as I know, there must be a case combinator for a given type in order for case syntax to work. Existing case combinators are e.g. case_option and case_list. Now, if something has not been defined as a datatype, there are other ways to obtain that combinator (e.g. the free_constructors) command. The int doesn't have that kind of setup.
simps_of_case transforms an equation of the form f x = P (case x of ...) into equations of the form f pat1 = P ..., f pat2 = P ..., ... If you're pattern matching on a term which is not a variable from the argument list, it can't do that. In your example, what did you expect to get generated?
I investigated Coq a little, with its dependent types. I have only the foggiest idea about it all, but now I have in mind that I want a bit vector as a bool list, in which the length of the vector is part of the type.
(This question is the possible predecessor of another question. In the next question, if I ask it, I'll ask whether I can recover what I lose, when I use typedef as below.)
(For this question, the question is at the bottom)
Here are the requirements for the type I want:
It has to use bool list, so that I can directly or indirectly do pattern matching and recursion on the list, and
the length of the vector has to be specified in the type.
Here is what I have:
typedef bitvec_4 = "{bl::bool list. length bl = 4}"
by(auto, metis Ex_list_of_length)
It's important that the length of the list be part of the type, because I want to use the type with a definition, where all lists are known to be of the same size, like with this simple example:
definition two_bv4_to_bv4 :: "bitvec_4 => bitvec_4 => bitvec_4" where
"two_bv4_to_bv4 x y = x"
What I don't want, in a theorem, is to have to specify the length of the lists. Type classes would eventually come into play somehow, but I want, as I say, the length to be specified in the type definition.
Definition and type signatures. Where do I let n = 4!!? (a tech joke of minimal humor-value)
Now, I try to generalize with a typedef like this, in which the length is a variable:
typedef bitvec_n = "{(bl::bool list, n::nat). length bl = n}"
by(auto)
That's no good. In a definition like this next one, my type doesn't guarantee that all lists are of the same length:
definition two_bvn_to_bvn :: "bitvec_n => bitvec_n => bitvec_n" where
"two_bvn_to_bvn x y = x"
The question? (I think so)
I've experimented a little with types like bitvec_4 above. If I don't run into big roadblocks, I might try to make big use of them.
I could define types like the above for powers of 2, up to, say, 1024 bits, along with type classes that reflect their common properties.
But, is there a better way to do this? It has to be somewhat straightforward, I think, with the use of bool list.
Update (got the answer for what it was actually about)
Based on Manuel's answer, I include here a self-contained theory.
It's mostly a duplication of Manuel's source, but at the end, my functions swap_bl and swap_2bv, along with the final use of value, show the end result of what I was trying to accomplish. My comments emphasize the problems that were on my mind, and possibly, my end application shows why I haven't looked to HOL/Word as a solution.
For a typedef type, to do pattern matching indirectly, similar to that with swap_bl and 2 bitvec, I was using the Abs and Rep functions together as inverses. One problem, as Manuel pointed out, is that I can feed an Abs function a bool list of the wrong length, and it won't give an error. Another big problem is the abstraction violations due to the use of the Abs function.
Those problems, and wanting to know if I could recover the use of value for my typedef type, would have been parts of my next question, but all that's been answered here.
theory i141210ac__testing_out_manuels_answer
imports Main "~~/src/HOL/Library/Numeral_Type"
begin
(*Basic type definition.*)
typedef ('n::finite) bitvec = "{bs :: bool list. length bs = CARD('n)}"
morphisms bitvec_to_list Abs_bitvec
by (simp add: Ex_list_of_length)
setup_lifting type_definition_bitvec
lift_definition nth :: "('n::finite) bitvec => nat => bool" (infixl "$" 90)
is List.nth .
(*Can't use 'value' yet for 'nth', or I get an abstraction violation.*)
term "(Abs_bitvec [True,False] :: 2 bitvec) $ 1"
(*Truncate or fill the list: needed to set things up for 'value'.*)
definition set_length :: "nat => bool list => bool list" where
"set_length n xs = (if length xs < n
then xs # replicate (n - length xs) False
else take n xs)"
lemma length_set_length [simp]: "length (set_length n xs) = n"
unfolding set_length_def by auto
definition list_to_bitvec :: "bool list => ('n::finite) bitvec" where
"list_to_bitvec xs = Abs_bitvec (set_length CARD('n) xs)"
(*Finishing the magic needed for 'value'.*)
lemma list_to_bitvec_code [code abstract]:
"bitvec_to_list (list_to_bitvec xs :: ('n::finite) bitvec)
= set_length CARD('n) xs"
unfolding list_to_bitvec_def by(simp add: Abs_bitvec_inverse)
(*Inverses for lists of length 2: no abstraction violations.*)
value "list_to_bitvec (bitvec_to_list x) :: 2 bitvec"
value "bitvec_to_list (list_to_bitvec x :: 2 bitvec)"
(*The magic now kicks in for 'value' and 'nth'. Understanding is optional.*)
value "(list_to_bitvec [True,False] :: 2 bitvec) $ 1" (*OUTPUT: False.*)
(*For my use, the primary context of all this is pattern matching on lists.
I can't pattern match on a 'typedef' type directly with 'fun', because
it's not a 'datatype'. I do it indirectly.*)
fun swap_bl :: "bool list => bool list" where
"swap_bl [a,b] = [b,a]"
|"swap_bl _ = undefined"
definition swap_2bv :: "2 bitvec => 2 bitvec" where
"swap_2bv bv = list_to_bitvec (swap_bl (bitvec_to_list bv))"
value "swap_2bv (list_to_bitvec [a,b] :: 2 bitvec)" (*
OUTPUT: "Abs_bitvec [b, a]" :: "2 bitvec" *)
(*Whether that's all a good idea, that's about the future, but it appears the
hard work, recovering the use of 'value', along with generalizing length,
has been done by Manuel, and the authors of Numeral_Type and its imports.*)
end
Isabelle does not support dependent types, but there are ways to still do what you want to do. For instance, there is already a stack of type classes and type syntax for type-level natural numbers.
theory Scratch
imports Main "~~/src/HOL/Library/Numeral_Type"
begin
lemma "(UNIV :: 4 set) = {0,1,2,3}"
by (subst UNIV_enum) eval
As you can see, the type 4 is a type that contains the numbers from 0 to 3. Incidentally, this can also be used for computations in modular arithmetic:
lemma "((2 + 3) :: 4) = 1" by simp
lemma "((2 * 3) :: 4) = 2" by simp
You can use these numeral types to parametrise your bit vectors with a length:
typedef ('n::finite) bitvec = "{bs :: bool list. length bs = CARD('n)}"
morphisms bitvec_to_list Abs_bitvec
by (simp add: Ex_list_of_length)
setup_lifting type_definition_bitvec
You can access the n-th element of a bit vector by lifting the nth function from Boolean lists to bit vectors, which works automatically:
lift_definition nth :: "('n::finite) bitvec ⇒ nat ⇒ bool" (infixl "$" 90) is List.nth .
Converting boolean lists to bit vectors is a bit tricky, because the list you get in might not have the correct length; the expression list_to_bitvec [True] :: 2 bitvec would typecheck, but is obviously problematic. You could solve this either by returning undefined or, perhaps more appropriate in this instance, filling up the list with False or truncating it to get the right length:
definition set_length :: "nat ⇒ bool list ⇒ bool list" where
"set_length n xs = (if length xs < n then xs # replicate (n - length xs) False else take n xs)"
lemma length_set_length[simp]: "length (set_length n xs) = n"
unfolding set_length_def by auto
Now we can define a function that converts a list of Booleans to a bit vector:
definition list_to_bitvec :: "bool list ⇒ ('n::finite) bitvec" where
"list_to_bitvec xs = Abs_bitvec (set_length CARD('n) xs)"
However, we are not allowed to use Abs_bitvec in code equations; if you tried to evaluate, say, list_to_bitvec [True] :: 1 bitvec, you would get an abstraction violation. We have to give an explicit code abstract equation in terms of the morphism list_to_bitvec:
lemma list_to_bitvec_code[code abstract]:
"bitvec_to_list (list_to_bitvec xs :: ('n::finite) bitvec) = set_length CARD('n) xs"
unfolding list_to_bitvec_def by (simp add: Abs_bitvec_inverse)
And now we are basically done and can do e.g. this:
definition myvec :: "4 bitvec" where "myvec = list_to_bitvec [True, False, True]"
value myvec
(* Output: "Abs_bitvec [True, False, True, False]" :: "4 bitvec" *)
value "myvec $ 2"
(* Output: "True" :: "bool" *)
Note that you always have to annotate the result of list_to_bitvec with its length; Isabelle can not infer the length.
You may also want to have a look at the Word theory in ~~/src/HOL/Word/; it implements machine words of fixed length with all kinds of bit operations like NOT, AND, OR, etc.:
value "42 AND 23 :: 32 word"
(* Output: "2" :: "32 word" *)
value "293 :: 8 word"
(* Output: "37" :: "8 word" *)
value "test_bit (42 :: 8 word) 1"
(* Output: "True" :: "bool" *)
value "set_bit (42 :: 8 word) 2 True"
(* Output: "46" :: "8 word" *)
value "(BITS i. i < 4) :: 8 word"
(* Output: "15" :: "8 word" *)
Another related type are the vectors in src/HOL/Library/Multivariate_Analysis/Finite_Cartesian_Product.