Locale import two classes - isabelle

I want to define classes and locales and combine then to create different types My attempt is given by
theory Scratch
imports Main
begin
class A =
fixes getA:: "'a ⇒ string"
class B =
fixes getB:: "'a ⇒ string"
locale CombAB = A + B +
fixes get:: "'a ⇒ string"
end
the results is
locale CombAB =
fixes getA :: "'b ⇒ char list"
and getB :: "'c ⇒ char list"
and get :: "'a ⇒ char list"
but I expected
locale CombAB =
fixes getA :: "'a ⇒ char list"
and getB :: "'a ⇒ char list"
and get :: "'a ⇒ char list"
Why are there three variables 'a,'b,'c and not just one?

Unless told otherwise, Isabelle always derives most general types. In this particular example it has no idea that A, B and CombAB are talking about the same type 'a, so it just renames type variables to the new ones. You can tell it to use the same type variable by specifying required types explicitly with a clause for as follows:
locale CombAB =
A getA +
B getB
for
getA:: "'a ⇒ string" and
getB:: "'a ⇒ string" +
fixes
get:: "'a ⇒ string"

Related

Is it a good idea to extend standard types and operations?

I need to defined an extended bool type (ebool = bool ∪ {⊥}) and a set of operations for the type (conjunction, etc.).
Here is the theory:
theory EboolTest
imports Main "~~/src/HOL/Library/Adhoc_Overloading"
begin
notation
bot ("⊥")
declare [[coercion_enabled]]
typedef ebool = "UNIV :: bool option set" ..
definition ebool :: "bool ⇒ ebool" where
"ebool b = Abs_ebool (Some b)"
declare [[coercion "ebool :: bool ⇒ ebool"]]
instantiation ebool :: bot
begin
definition "⊥ ≡ Abs_ebool None"
instance ..
end
free_constructors case_ebool for
ebool
| "⊥ :: ebool"
apply (metis Rep_ebool_inverse bot_ebool_def ebool_def not_Some_eq)
apply (smt Abs_ebool_inverse ebool_def iso_tuple_UNIV_I option.inject)
by (simp add: Abs_ebool_inject bot_ebool_def ebool_def)
lemmas ebool2_cases = ebool.exhaust[case_product ebool.exhaust]
lemmas ebool3_cases = ebool.exhaust[case_product ebool.exhaust ebool.exhaust]
fun ebool_and :: "ebool ⇒ ebool ⇒ ebool" (infixr "∧" 35) where
"ebool_and a b = ebool (HOL.conj a b)"
| "ebool_and False _ = False"
| "ebool_and _ False = False"
| "ebool_and ⊥ _ = ⊥"
| "ebool_and _ ⊥ = ⊥"
no_notation HOL.conj (infixr "∧" 35)
consts "(∧)" :: "'a ⇒ 'a ⇒ 'a"
adhoc_overloading "(∧)" HOL.conj
adhoc_overloading "(∧)" ebool_and
end
The following works fine:
value "True ∧ (False::ebool)"
value "True ∧ ⊥"
But the following returns ebool, but I expect to see bool:
value "True ∧ False"
It seems my approach is bad. Could you suggest a better approach? Maybe I it's not good to overload standard operations at all?
First of all, I'm a little surprised that this:
consts "(∧)" :: "'a ⇒ 'a ⇒ 'a"
works at all. It sounds like a bug, because the (...) notation is reserved for the system. (To be fair, it does print a warning, and unless you know exactly what you're doing, it's unwise to ignore them.)
But to circle back to your actual problem, I don't think you should use adhoc_overloading to overload syntax that would otherwise be provided by Main.
There are alternatives. For example, you could use a bold version. This is used in this theory.
Or you could use another symbol, like &&.
As an addendum: I believe there might be weird interactions between coercions and adhoc overloading. Both tools are fine by themselves, but watch out if they interact with each other.

How to define an inductive predicate inside locale?

Here is an example of a simple locale:
locale test =
fixes test_less_eq :: "'a ⇒ 'a ⇒ bool"
begin
inductive test_eq where
"test_less_eq x y ⟹ test_less_eq y x ⟹ test_eq x y"
end
It defines inductive test_eq. It can be defined using definition, but I need it to be an inductive predicate.
Then I define a trivial interpretation of the locale and try to use it:
interpretation interp: test "op <" .
inductive some_pred where
"interp.test_eq x y ⟹
some_pred x y"
code_pred [show_modes] some_pred .
The problem is that I get the following error for code_pred:
Type mismatch of predicate test.test_eq (trying to match ?'a
⇒ ?'a ⇒ bool and ('a ⇒ 'a ⇒ bool)
⇒ 'a ⇒ 'a ⇒ bool) in ?x1 < ?y1 ⟹
?y1 < ?x1 ⟹ interp.test_eq ?x1 ?y1
What is a cause of the error and how to fix it?
The predicate compiler has never been localized, i.e., it cannot directly deal with predicates that are defined inside a locale. There are two ways to make this work nevertheless.
Either, use global_interpretation with a defines clause to introduce a new constant for the predicate (plain interpretation only introduces an abbreviation). Then, you also have to re-declare the introduction rules to code_pred and prove the corresponding elimination rule.
global_interpretation interp: test "op <"
defines interp_test_eq = interp.test_eq .
declare interp.test_eq.intros[code_pred_intro]
code_pred interp_test_eq by(rule interp.test_eq.cases)
Or, leave the interpretation as is and re-declare the introduction rules of the internal constant to which the definition in the locale is mapped. This is <locale_name>.<predicate_name>, i.e., test.test_eq in your case. This works only if your locale has no assumption.
declare test.test_eq.intros[code_pred_intro]
code_pred test.test_eq by(rule test.test_eq.cases)

How to use classes or locales?

I'm trying to define a generic operations for a programming language:
type_synonym vname = "string"
type_synonym 'a env = "vname ⇒ 'a option"
locale language =
fixes big_step :: "'exp × 'val env ⇒ 'val ⇒ bool" (infix "⇒" 55)
fixes typing :: "'type env ⇒ 'exp ⇒ 'type ⇒ bool" ("(1_/ ⊢/ (_ :/ _))" [50,0,50] 50)
For example this is a particular language:
datatype foo_exp =
FooBConst bool |
FooLet vname foo_exp foo_exp |
FooVar vname |
FooAnd foo_exp foo_exp
datatype foo_val = FooBValue bool | FooIValue int
type_synonym foo_env = "foo_val env"
datatype foo_type = FooBType | FooIType
type_synonym foo_tenv = "foo_type env"
inductive foo_big_step :: "foo_exp × foo_env ⇒ foo_val ⇒ bool"
inductive foo_typing :: "foo_tenv ⇒ foo_exp ⇒ foo_type ⇒ bool"
How to make it an instance of language locale?
Is it possible to use same notation (⇒ and _ ⊢ _ : _) for different languages in one theory? Could this notation be polymorphic?
To specialize the parameters of a locale, you need to do an interpretation as in
interpretation foo: language foo_big_step foo_typing .
This will generate an abbreviation foo.f for every definition f in the locale language specialised to foo_big_step and foo_typing and every theorem thm of language becomes specialised to foo.thm. The mixfix syntax annotations of parameters and all constants in the locale will not be inherited.
Type classes cannot be used in this context because your locale depends on multiple type variables and type classes in Isabelle support only exactly one type variable.
If you want to use some kind of polymorphic notation for the big-step semantics and type judgements, Adhoc_Overloading might work, provided that Isabelle's parser can statically resolve the overloading uniquely. Here's how this might work:
theory Language imports Main "~~/src/Tools/Adhoc_Overloading" begin
type_synonym 'a env = "vname ⇒ 'a option"
consts
big_step :: "'exp × 'val env ⇒ 'val ⇒ bool" (infix "⇒" 55)
typing :: "'type env ⇒ 'exp ⇒ 'type ⇒ bool" ("(1_/ ⊢/ (_ :/ _))" [50,0,50] 50)
locale language =
fixes big_step :: "'exp × 'val env ⇒ 'val ⇒ bool"
fixes typing :: "'type env ⇒ 'exp ⇒ 'type ⇒ bool"
begin
adhoc_overloading Language.big_step big_step
adhoc_overloading Language.typing typing
end
After the interpretation, you have to register foo's semantics and type judgement constants foo_big_step and foo_typing for adhoc overloading with the syntactic constants big_step and typing again.
interpretation foo: language foo_big_step foo_typing .
adhoc_overloading Language.big_step foo_big_step
adhoc_overloading Language.typing foo_typing
So when you write
term "(x :: foo_exp, E) ⇒ v"
thereafter, Isabelle's parser will figure out by the types that this refers to foo_big_step, and inside the locale Language, term "(x :: 'exp, E) ⇒ v" is resolved to the locale parameter big_step.
This should also work for multiple interpretations of the locale Language provided that the types are sufficient to uniquely resolve the overloading. If not, you'll get error messages, which are not always easy to understand.

Isabelle and class overloading

Two classes are defined containing the same function, but when the two classes is used in a locale there is an unification error:
theory Scratch
imports Main
begin
class c1 =
fixes getName :: "'a ⇒ string"
class c2 =
fixes getName :: "'a ⇒ string"
locale c12 =
fixes match :: "('a::c1) ⇒ ('b::c2) ⇒ bool"
assumes as : "match a b ⟶ (getName a) = (getName b)"
end
The unification error is resolved by renaming (getName b) to (getName_b b) and use the class definition
class c2 =
fixes getName_b :: "'a ⇒ string"
Does a solution exist without renaming?
Here a solution is given when the overloading is needed when datatypes are parameters.
Fully or partially qualified identifiers can be used. I used find_consts, as shown below, to find the qualified names of the type class constants.
The type inference only required that I use c1_class.getName a to get rid of the error.
theory Scratch
imports Complex_Main
begin
class c1 =
fixes getName :: "'a => string"
class c2 =
fixes getName :: "'a => string"
find_consts name: getName (*
find_consts
name: "getName"
found 2 constant(s):
Scratch.c1_class.getName :: "'a => char list"
Scratch.c2_class.getName :: "'a => char list"
*)
declare[[show_sorts]]
locale c12 =
fixes match :: "('a::c1) => ('b::c2) => bool"
assumes as : "match a b --> (c1_class.getName a) = (getName b)"
end

Exporting code from locales with multiple parameters

according to the codegen documentation section "7.3 Locales and interpretation", exporting code from locales is a bit tricky but achievable. The following example works fine:
locale localTest =
fixes A :: "string"
begin
fun concatA :: "string ⇒ string" where "concatA x = x#A"
definition concatAA :: "string ⇒ string" where "concatAA x = x#A#A"
end
definition localtest_concatA :: "string ⇒ string " where
[code del]: "localtest_concatA = localTest.concatA ''a''"
definition localtest_concatAA :: "string ⇒ string " where
[code del]: "localtest_concatAA = localTest.concatAA ''a''"
interpretation localTest "''a''"
where "localTest.concatA ''a'' = localtest_concatA"
and "localTest.concatAA ''a'' = localtest_concatAA"
apply unfold_locales
apply(simp_all add: localtest_concatA_def localtest_concatAA_def)
done
export_code localtest_concatA localtest_concatAA in Scala file -
How can I export code for locales with multiple parameters? Given the following locale:
locale localTest =
fixes A :: "string"
fixes B :: "string"
begin
fun concatA :: "string ⇒ string" where "concatA x = x#A"
definition concatB :: "string ⇒ string" where "concatB x = x#B"
end
I can interpret it with
interpretation localTest "''a''" "''b''" .
But I canot use this interpretation in a definition
definition localtest_concatA :: "string ⇒ string " where
[code del]: "localtest_concatA = localTest.concatA ''a'' ''b''"
It fails with
Type unification failed: Clash of types "_ list" and "_ ⇒ _"
Type error in application: incompatible operand type
Operator: op = localtest_concatA :: (char list ⇒ char list) ⇒ bool
Operand: localTest.concatA ''a'' ''b'' :: char list
Look at your introduced constants, e.g., by the term command. We have
term localTest.concatA
with output
"localTest.concatA" :: "char list ⇒ char list ⇒ char list"
You see that in addition to the single parameter that you gave in the original definition (inside the locale), there is an additional one (but only 1 not 2, since the definition does not rely on B).
Now, after your interpretation (since you did not explicitly provide a name, the constants of localTest will be in scope without qualifier) we have
term concatA
with output
"localTest.concatA ''a''" :: "char list ⇒ char list"
That is, localTest.concatA ''a'' is already of type string => string. You additionally add ''b'' and obtain type string, but your type annotation says string => string. So there is really a clash of types and the reason was that you gave too many arguments to localTest.concatA. Try using
definition localtest_concatA :: "string ⇒ string " where
[code del]: "localtest_concatA = concatA
instead.

Resources