How to use Isabelle's code_pred and values commands? - isabelle

There is an example of code_pred usage in "Concrete Semantics" by Tobias Nipkow and Gerwin Klein, Section 7.2.2. I've implemented a simple language based on the examples and I try to calculate values of expressions:
theory BooLang
imports Main
begin
type_synonym id = string
type_synonym 'a Env = "id ⇒ 'a"
datatype BooBoolExp =
BooLiteralExp bool |
BooLetExp id BooBoolExp BooBoolExp |
BooVarExp id |
BooAndExp BooBoolExp BooBoolExp
datatype BooVal = Bv bool
type_synonym BooEnv = "BooVal Env"
inductive tbooval :: "BooBoolExp × BooEnv ⇒ BooVal ⇒ bool" (infix "⇒" 55) where
Literal: "(BooLiteralExp b, env) ⇒ Bv b" |
And: "⟦(a, env) ⇒ Bv av; (b, env) ⇒ Bv bv⟧ ⟹ (BooAndExp a b, env) ⇒ Bv (av ∧ bv)" |
Var: "(BooVarExp v, env) ⇒ env v" |
Let: "⟦(val, env) ⇒ b; (body, env(v := b)) ⇒ x⟧ ⟹ (BooLetExp v val body, env) ⇒ x"
code_pred tbooval .
values "{t. True}"
values "{t. (BooLiteralExp true, λ_. Bv false) ⇒ t}"
end
But for the 1st values' invocation I get the error:
Evaluation with values is not possible because compilation with
code_pred was not invoked.
And for the 2nd one I get the error:
No mode possible for comprehension {t. (BooLiteralExp true, λ_. Bv
false) ⇒ t}.
What's wrong with my theory?

The first command values {t. True} cannot work as this command asks Isabelle to enumerate all values of the type of t, which is inferred to be a type variable 'a; and this cannot be done.
For the second command, you just misspelled True and False. As is, true and false are inferred to be boolean variables instead of boolean constants. Unlike value, the command values does not support symbolic execution in Isabelle2016-1. That is, all input arguments to the inductive predicate must be ground values without variables. In your example, code_pred infers two execution modes: One in which everything is given as input and one in which only the first argument is given as input. You can see the inferred modes by passing the [show_modes] option as in
code_pred [show_modes] tboolval .
You can find some further documentation on code_pred and values in the code generator tutorial.

Related

How to use type's argument as a value in a function?

I'm trying to define a simple type system with a custom set type:
notation bot ("⊥")
typedef 'a myset = "UNIV :: 'a fset option set" ..
copy_bnf 'a myset
setup_lifting type_definition_myset
lift_definition myset :: "'a fset ⇒ 'a myset" is Some .
instantiation myset :: (type) bot
begin
lift_definition bot_myset :: "'a myset" is None .
instance ..
end
free_constructors case_myset for
myset
| "⊥ :: 'a myset"
apply (simp_all add: bot_myset_def myset_def)
apply (metis Rep_myset_inverse option.collapse)
apply (metis Abs_myset_inverse iso_tuple_UNIV_I option.inject)
apply (metis Abs_myset_inverse iso_tuple_UNIV_I option.distinct(1))
done
lift_definition map_myset :: "('a ⇒ 'b) ⇒ 'a myset ⇒ 'b myset"
is "map_option ∘ fimage" .
Type system has integer, real and set types:
datatype type =
IType
| RType
| SType type
Also there are 3 kinds of values:
datatype val =
IVal int
| RVal real
| SVal "val myset"
I need to define a function type_of which determines a type of a value:
fun elem_type :: "val myset ⇒ type" and
type_of :: "val ⇒ type" where
"elem_type xs = ???"
| "type_of (IVal x) = IType"
| "type_of (RVal x) = RType"
| "type_of (SVal x) = SType (elem_type x)"
value "type_of (SVal (myset {|IVal 1, IVal 2|}))"
value "type_of (SVal (myset {|RVal 1, RVal 2|}))"
value "type_of (SVal (myset {|SVal (myset {|IVal 1|})|}))"
Could you suggest how to get element type of myset?
I would say there is a fundamental problem with your modelling: your type_of function seems to suggest that every value has exactly one type.
In his comment, Lars has already pointed out that there are some values (e.g. a set containing both integers and real values) that you would probably consider to be invalid and that therefore have no type at all. That you could handle by returning ‘undefined’ or something like that, perhaps, and introduce some kind of additional ‘well-formedness’ predicate for values, but I'm not sure that's a good idea.
Another problem is that there are some values that are polymorphic, i.e. that have more than one type, in a sense: For instance, what should your type_of function return for the empty set, i.e. SVal (myset {|})? There are infinitely many reasonable types that this thing could have.
The usual approach to handle this is to introduce a typing relation instead of a typing function so that each value can have several types – or none at all. Usually, you do this with the inductive command, defining an inductive predicate like has_type v t which states that the value v has the type t (or ‘can be given the type t’ in case of polymorphism).
Also, on an unrelated note, I am not sure why you define a new type and use copy_bnf, free_constructors etc. I don't know what you're planning to do but my guess would be that whatever it is does probably not get easier with that typedef; it will probably just cause some annoyances.

Isabelle unification error

I am new to Isabelle and this is a simplification of my first program
theory Scratch
imports Main
begin
record flow =
Src :: "nat"
Dest :: "nat"
record diagram =
DataFlows :: "flow set"
Transitions :: "nat set"
Markings :: "flow set"
fun consume :: "diagram × (nat set) ⇒ (flow set)"
where
"(consume dia trans) = {n . n ∈ (Markings dia) ∧ (∃ t ∈ trans . (Dest n) = t)}"
end
The function give the error:
Type unification failed: Clash of types "_ ⇒ " and " set"
Type error in application: operator not of function type
Operator: consume dia :: flow set
Operand: trans :: (??'a × ??'a) set ⇒ bool
What should I do for the the code to compile?
First of all, you give two parameters to your consume function, but the way you defined its type, it takes a single tuple. This is unusual and often inconvenient – defined curried functions instead, like this:
fun consume :: "diagram ⇒ (nat set) ⇒ (flow set)"
Also, trans is a constant; it is the property that a relation is transitive. You can see that by observing that trans is black to indicate that it is a constant and the other variable is blue, indicating that it is a free variable.
I therefore recommend using another name, like ts:
where
"consume dia ts = {n . n ∈ (Markings dia) ∧ (∃ t ∈ ts . (Dest n) = t)}"

How to generate code for reverse sorting

What is the easiest way to generate code for a sorting algorithm that sorts its argument in reverse order, while building on top of the existing List.sort?
I came up with two solutions that are shown below in my answer. But both of them are not really satisfactory.
Any other ideas how this could be done?
I came up with two possible solutions. But both have (severe) drawbacks. (I would have liked to obtain the result almost automatically.)
Introduce a Haskell-style newtype. E.g., if we wanted to sort lists of nats, something like
datatype 'a new = New (old : 'a)
instantiation new :: (linorder) linorder
begin
definition "less_eq_new x y ⟷ old x ≥ old y"
definition "less_new x y ⟷ old x > old y"
instance by (default, case_tac [!] x) (auto simp: less_eq_new_def less_new_def)
end
At this point
value [code] "sort_key New [0::nat, 1, 0, 0, 1, 2]"
yields the desired reverse sorting. While this is comparatively easy, it is not as automatic as I would like the solution to be and in addition has a small runtime overhead (since Isabelle doesn't have Haskell's newtype).
Via a locale for the dual of a linear order. First we more or less copy the existing code for insertion sort (but instead of relying on a type class, we make the parameter that represents the comparison explicit).
fun insort_by_key :: "('b ⇒ 'b ⇒ bool) ⇒ ('a ⇒ 'b) ⇒ 'a ⇒ 'a list ⇒ 'a list"
where
"insort_by_key P f x [] = [x]"
| "insort_by_key P f x (y # ys) =
(if P (f x) (f y) then x # y # ys else y # insort_by_key P f x ys)"
definition "revsort_key f xs = foldr (insort_by_key (op ≥) f) xs []"
at this point we have code for revsort_key.
value [code] "revsort_key id [0::nat, 1, 0, 0, 1, 2]"
but we also want all the nice results that have already been proved in the linorder locale (that derives from the linorder class). To this end, we introduce the dual of a linear order and use a "mixin" (not sure if I'm using the correct naming here) to replace all occurrences of linorder.sort_key (which does not allow for code generation) by our new "code constant" revsort_key.
interpretation dual_linorder!: linorder "op ≥ :: 'a::linorder ⇒ 'a ⇒ bool" "op >"
where
"linorder.sort_key (op ≥ :: 'a ⇒ 'a ⇒ bool) f xs = revsort_key f xs"
proof -
show "class.linorder (op ≥ :: 'a ⇒ 'a ⇒ bool) (op >)" by (rule dual_linorder)
then interpret rev_order: linorder "op ≥ :: 'a ⇒ 'a ⇒ bool" "op >" .
have "rev_order.insort_key f = insort_by_key (op ≥) f"
by (intro ext) (induct_tac xa; simp)
then show "rev_order.sort_key f xs = revsort_key f xs"
by (simp add: rev_order.sort_key_def revsort_key_def)
qed
While with this solution we do not have any runtime penalty, it is far too verbose for my taste and is not easily adaptable to changes in the standard code setup (e.g., if we wanted to use the mergesort implementation from the Archive of Formal Proofs for all of our sorting operations).

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.

code_pred in locales

I want to create an executable inductive within a locale. Without the locale everything works fine:
definition "P a b = True"
inductive test :: "'a ⇒ 'a ⇒ bool" where
"test a a" |
"test a b ⟹ P b c ⟹ test a c"
code_pred test .
However, when I try the same in a locale, it does not work:
locale localTest
begin
definition "P' a b = True"
inductive test' :: "'a ⇒ 'a ⇒ bool" where
"test' a a" |
"test' a b ⟹ P' b c ⟹ test' a c"
code_pred test'
end
The code_pred line in the locale returns the following error:
Not a constant: test'
You can give alternative introduction rules (see isabelle doc codegen section 4.2: Alternative introduction rules) and thereby avoid an interpretation. This also works for locales with parameters (and even for constants that are not defined inductively). A variant of your example having a parameter:
locale l =
fixes A :: "'a set"
begin
definition "P a b = True"
inductive test :: "'a ⇒ 'a ⇒ bool" where
"a ∈ A ⟹ test a a" |
"test a b ⟹ P b c ⟹ test a c"
end
We introduce a new constant
definition "foo A = l.test A"
And prove its introduction rules (thus the new constant is sound w.r.t. the old one).
lemma [code_pred_intro]:
"a ∈ A ⟹ foo A a a"
"foo A a b ⟹ l.P b c ⟹ foo A a c"
unfolding foo_def by (fact l.test.intros)+
Finally, we have to show that the new constant is also complete w.r.t. the old one:
code_pred foo by (unfold foo_def) (metis l.test.simps)
Expressed sloppily, locales are an abstraction mechanism that allows to introduce new constants relative to some hypothetical constants satisfying hypothetical properties, whereas code generation is more concrete (you need all the information that is required to implement a function, not just its abstract specification).
For that reason you first need to interpret a locale, before you can generate code. Of course, in your example there are no hypothetical constants and properties, thus an interpretation is trivial
interpretation test: localTest .
After that, you can use
code_pred test.test' .
Complete guess here, but I'm wondering if renaming test to test' has messed things up for you. (Consider changing code_pred test' to code_pred "test'".)

Resources