I would like to translate the following type declaration into Isabelle/HOL.
The definitions above serves for evaluating a modal formula in the basic modal language. I defined formula, frame and model in Isabelle as follows. (Is everything good up to now?)
datatype form = VAR "num"
| FALSE
| DISJ "form" "form"
| NOT "form"
| DIAM "form"
record 'a frame =
world :: "'a ⇒ bool"
rel :: "'a ⇒ 'a ⇒ bool"
record 'a model = "'a frame" +
valt :: "num ⇒ 'a ⇒ bool"
But then I think I get confused on the translation of the "satis" predicate. It seems to be a bunch of options. There are primrec, fun and inductive. Is there any consideration that I should take before picking one?
I am attempting to do "fun". I wrote:
fun satis :: "'a model ⇒ 'a ⇒ form ⇒ bool"
where
"satis M w (VAR p) = M.valt p v ∧ M.frame.world w
| satis M w FALSE = false"
And it already started complaining:
image.png
May I please ask if there is any quick fix? Thanks in advance!
I'm not so familiar with record syntax in Isabelle, but that should be valt M
Yong Kiam said:
I'm not so familiar with record syntax in Isabelle, but that should be
valt M
Thanks so much! The underscore before disappears.
Now I type (world (frame M))
and it does not look correct:
image.png
The "frame" is green, and may I please ask a "human language" translation of the error message?
= is binding to strongly. Use statis M w ... <--> ...
or statis M w ... = (...)
And it is False
instead of false
Thanks! I changed it into False and <-->.
image.png
Any clue why it thinks "frame" is green? M.frame also does not work.
Not obvious from https://isabelle.in.tum.de/doc/isar-ref.pdf how can we extract fields of a record term.
frame is a type, you cannot use it as a function in a term
I do not even know what you are trying to achieve
I would like the effect to be "a model consists of a frame and a valuation". So I should be able to type M.frame to extract the field "frame" from "M" and then type (M.frame).world to extract the field "world" from the frame "M.frame". But your comment does give a hint! I have just figured out the type "model" will not take "frame" as its field. So what I need is just "world M". Thank you!
For the type declaration, I wrote:
record 'a frame =
world :: "'a ⇒ bool"
rel :: "'a ⇒ 'a ⇒ bool"
record 'a model = "'a frame" +
valt :: "num ⇒ 'a ⇒ bool"
The same recipe of writing rel M to be the field "rel" of M does not seem to work.
Sorry if it turns out to be stupid, but I am confused again.
It seems that Isabelle does not complain about rel M w v, it is complaining about te quantification.
What's wrong here?
add parentheses. And binds too strongly
Thank you for the swift answer, it solves my problem.
image.png
Now Isabelle is happy.
(But I feel its strength to this extent is a bit weird.)
Regarding what to choose to use among fun/definition/primrec. I find an answer here:
https://courses.grainger.illinois.edu/cs576/sp2015/lectures/3-4/04-Isabelle-syntax-2x3.pdf slides 5.
usually definition for stuff that does not recurse, fun for everything else
there is rarely a reason to use primrec
Got it. I will just not use it for the foreseeable future then.
Last September, Jan van Brügge answered a question about fun/definition/primrec with the advice "usually definition
for stuff that does not recurse, fun
for everything else". I note from the Isar reference manual (p. 221) that
Also note that definitional packages like
datatype, primrec, fun
routinely declare Simplifier rules to the target context, while plaindefinition
is an exception in not declaring anything.
Does this mean that I should be changing
definition parallel::"'l ⇒ 'l ⇒ bool" (infix "||" 5) where
"parallel l m = (if (l ∈ Lines ∧ m ∈ Lines)
then l = m ∨ ¬ (∃ P. P ∈ Points ∧ meets P l ∧ meets P m) else undefined)"
to
definition parallel::"'l ⇒ 'l ⇒ bool" (infix "||" 5) where [simp]:
"parallel l m = (if (l ∈ Lines ∧ m ∈ Lines)
then l = m ∨ ¬ (∃ P. P ∈ Points ∧ meets P l ∧ meets P m) else undefined)"
perhaps? Or is adding non-recursive function-definitions to the simplifier a bad idea in general? Is there any general advice here?
Like most things, it depends. If you just want to use the definition as a shorthand, but want to automation to always look under it you can do that however at that point I would probably use abbreviation
instead.
With a definition you can define theorems about the definition where you can be sure that the automation will actually be able to apply them as there is no way that the definition got expanded too early. If you do want to expand the definition for a theorem, you can just add it to the simpset for that particular spot.
Remark that I sometimes go the opposite way and I use fun with [simp del]
to not automatically get the unfolding
fun test where
[simp del]: ‹test (a, b) = a + b›
lemmas test_def = test.simps
lemma ‹test (A, 0) = A›
by (simp add: test_def)
Following up on this, I tried to substitute a definition for a fun
and it seems to break my locale definition. Here's a version with fun
, which checks out OK, but if you comment that out and include the definition
version, you get a type error. This baffles me (as, frankly, does anything that mentions weak types).
theory Affine_test
imports Complex_Main
begin
locale affine_plane_data =
fixes Points :: "'p set" and Lines :: "'l set" and meets :: "'p ⇒ 'l ⇒ bool"
fixes join:: "'p ⇒ 'p ⇒ 'l"
fixes find_parallel:: "'l ⇒ 'p ⇒ 'l"
begin
fun parallel :: "'l ⇒ 'l ⇒ bool" (infix "||" 50) where
"l || m = (if (l ∈ Lines ∧ m ∈ Lines)
then l = m ∨ ¬ (∃ P. P ∈ Points ∧ meets P l ∧ meets P m) else undefined)"
(* replace the "fun" version of "parallel" with this one and see the next locale-description fail.*)
(*
definition parallel::"'l ⇒ 'l ⇒ bool" (infix "||" 5) where
"parallel l m = (if (l ∈ Lines ∧ m ∈ Lines)
then l = m ∨ ¬ (∃ P. P ∈ Points ∧ meets P l ∧ meets P m) else undefined)"*)
fun collinear :: "'p ⇒ 'p ⇒ 'p ⇒ bool"
where "collinear A B C = (if A ∈ Points ∧ B ∈ Points ∧ C ∈ Points
then (∃ l. l ∈ Lines ∧ meets A l ∧ meets B l ∧ meets C l) else undefined)"
end
locale affine_plane =
affine_plane_data +
assumes
a1a: "⟦P ≠ Q; P ∈ Points; Q ∈ Points⟧ ⟹ join P Q ∈ Lines ∧ meets P (join P Q) ∧ meets Q (join P Q)" and
a1b: "⟦P ≠ Q; P ∈ Points; Q ∈ Points; meets P m; meets Q m⟧ ⟹ m = join P Q" and
a2: "⟦¬ meets P l; P ∈ Points; l ∈ Lines⟧ ⟹ find_parallel l P ∈ Lines ∧ ( find_parallel l P) || l ∧ meets P (find_parallel l P)" and
a3: "∃P Q R. P ∈ Points ∧ Q ∈ Points ∧ R ∈ Points ∧ P ≠ Q ∧ P ≠ R ∧ Q ≠ R ∧ ¬ (collinear P Q R)"
begin
end
end
Can someone explain this? And perhaps more usefully, can someone explain how I might work out for myself what's happening in situations like this in the future?
It is useful when you want pattern matching
I do not see an error, which Isabelle version is that?
I just replace fun by definition and it worked
Copy-paste error:
fun parallel :: "'l ⇒ 'l ⇒ bool" (infix "||" 50) where
definition parallel::"'l ⇒ 'l ⇒ bool" (infix "||" 5) where
50 vs 5
John Hughes said:
Can someone explain this? And perhaps more usefully, can someone explain how I might work out for myself what's happening in situations like this in the future?
I got lucky to see that definition but in general I reduce the term to become as small as possible, here: term ‹f || l ∧ H›
Then from the error message
Type unification failed
Type error in application: incompatible operand type
Operator: (||) f :: 'l ⇒ bool
Operand: l ∧ H :: bool
you can see how it is parsed
Actually not a copy-paste error --- the "definition" version comes from a different (working!) copy of very similar code, in which I reduced the priority for some reason that presumably felt very good at the time. :) Presumably there are other differences that made things work in that copy. And it's nice to know that the problem was benign, and simple enough that I could have figured it out myself if I hadn't been spooked by the warning about weak types.
Thanks!
Last updated: Feb 01 2025 at 20:19 UTC