I am trying to understand how "inheritance" work at locale/interpretation level. I have an abstract locale (locA), with a method (g) defined within it. I have a concrete locale (locB), which is an instance/model of the abstract one. Is there a way to use the g
method in such a way that I don't get a "type unification" error?
Below is a toy example of what I am trying to achieve
theory InstOverwrite
imports Main
begin
locale locA =
fixes f :: "'a ⇒ 'b"
begin
definition g :: "'a set ⇒ 'b set" where
"g X = f`X"
end
locale locB =
fixes fb :: "nat ⇒ nat"
assumes fb_def: "fb n = n*2"
begin
interpretation locA
apply unfold_locales
done
end
context locB
begin
definition fx :: "nat set ⇒ nat set" where
"fx X = {n+1 | n::nat. n ∈ locA.g X}"
end
end
Unsurprisingly, I get the following error:
Type unification failed: Clash of types "_ set" and "_ ⇒ _"
Type error in application: incompatible operand type
Operator: locA.g :: (??'a ⇒ ??'b) ⇒ ??'a set ⇒ ??'b set
Operand: X :: nat set
Within your code, you have not actually connected the two locales.
locA.g
exists “outside” the locale context in the sense that it must be supplied an f :: "'a ⇒ 'b"
in order to be used. This is what Isabelle tries to express by stressing Operator: locA.g :: (??'a ⇒ ??'b) ⇒ ??'a set ⇒ ??'b set
. So, in order to make the type checker happy here, you would need to explicitly say that fb
should serve as f
in the function application.
definition fx :: "nat set ⇒ nat set" where
"fx X = {n+1 | n::nat. n ∈ locA.g fb X}"
(The only difference is writing locA.g fb X
instead of locA.g X
.)
interpretation
From your description of the problem, I gather that you hoped interpretation locA
would have taken care of establishing this connection. This is not the case.
To establish the instantiation that f := fb
, you would need to supply parameters to the interpretation. The following would work:
interpretation locA fb .
definition fx :: "nat set ⇒ nat set" where
"fx X = {n+1 | n::nat. n ∈ g X}"
(Note that it's now g X
instead of locA.g fb X
!)
sublocale
?I conveniently dropped the end/begin context from the example script. The reason being that the context jumps would kill the interpretation. If you want the connection to persist across contexts, the sublocale
keyword is more appropriate:
sublocale "locA" fb .
end (* of locale context locB *)
(* somewhere else we may add to the locale again: *)
definition (in locB) fy :: "nat set ⇒ nat set" where
"fy X = {n+1 | n::nat. n ∈ g X}"
Depending on what you're actually wanting to do, an even more different formulation might make sense. I assume that the listing is just the way it is to have some minimal example.
For people bumping into this question, I want to note that the locale inheritance situation here might be expressed more conveniently by one of the two following approaches:
locB
extend locA
:The following will use the same f
for both locales directly and just add the defining assumption:
locale locB =
locA f for f :: "nat ⇒ nat" +
assumes f_def: "f n = n*2"
locB
instantiate parameters for locA
definition fb :: "nat ⇒ nat" where "fb n ≡ n * 2"
locale locB =
locA fb