Theory RG_Syntax_Extensions
section ‹Rely-Guarantee (RG) Syntax Extensions›
text ‹The core extensions to the built-in RG library:
improved syntax of RG sentences in the quintuple- and
keyword-styles, with data-invariants.
Also: subgoal-generating methods for RG inference-rules that work
with the structured proof-language, Isar.›
theory RG_Syntax_Extensions
imports
"HOL-Hoare_Parallel.RG_Syntax"
"HOL-Eisbach.Eisbach"
begin
text ‹We begin with some basic notions that are used later on.›
text ‹Notation for forward function-composition:
defined in the built-in @{text ‹Fun.thy›} but disabled at the
end of that theory.
%
This operator is useful for modelling atomic primitives such as Swap
and Fetch-And-Increment, and also useful when coupling concrete- and
auxiliary-variable instructions.›
notation fcomp (infixl "∘>" 60)
lemmas definitions [simp] =
stable_def Pre_def Rely_def Guar_def Post_def Com_def
text ‹In applications, guarantee-relations often stipulates that Thread
@{text i} should ``preserve the rely-relations of all other threads''.
This pattern is supported by the following higher-order function, where
@{text j} ranges through all the threads that are not @{text i}.›
abbreviation for_others :: "('index ⇒ 'state rel) ⇒ 'index ⇒ 'state rel" where
"for_others R i ≡ ⋂ j ∈ -{i}. R j"
text ‹Relies and guarantees often state that certain variables remain
unchanged. We support this pattern with the following syntactic sugars.›
abbreviation record_id :: "('record ⇒ 'field) ⇒ 'record rel"
("id'(_')" [75] 74) where
"id(c) ≡ ⦃ ªc = ºc ⦄"
abbreviation record_ids :: "('record ⇒ 'field) set ⇒ 'record rel"
("ids'(_')" [75] 74) where
"ids(cs) ≡ ⋂ c ∈ cs. id(c)"
abbreviation record_id_indexed ::
"('record ⇒ 'index ⇒ 'field) ⇒ 'index ⇒ 'record rel"
("id'( _ @ _ ')") where
"id(c @ self) ≡ ⦃ ºc self = ªc self ⦄"
abbreviation record_ids_indexed ::
"('record ⇒ 'index ⇒ 'field) set ⇒ 'index ⇒ 'record rel"
("ids'( _ @ _ ')") where
"ids(cs @ self) ≡ ⋂ c ∈ cs. id(c @ self)"
text ‹The following simple method performs an optional simplification-step,
and then tries to apply one of the RG rules, before attempting to discharge
each subgoal using @{text force}.
%
This method works well on simple RG sentences.›
method method_rg_try_each =
(clarsimp | simp)?,
( rule Basic | rule Seq | rule Cond | rule While
| rule Await | rule Conseq | rule Parallel);
force+
subsection ‹Lifting of Invariants›
text ‹There are different ways to combine the invariant with the rely
or guarantee, as long as the invariant is preserved.
%
Here, a rely- or guarantee-relation @{term R} is combined with the
invariant @{term I} into @{term ‹{(s,s'). (s ∈ I ⟶ s' ∈ I) ∧ R}›}.›
definition pred_to_rel :: "'a set ⇒ 'a rel" where
"pred_to_rel P ≡ {(s,s') . s ∈ P ⟶ s' ∈ P}"
definition invar_and_guar :: "'a set ⇒ 'a rel ⇒ 'a rel" where
"invar_and_guar I G ≡ G ∩ pred_to_rel I"
lemmas simp_defs [simp] = pred_to_rel_def invar_and_guar_def
subsection ‹RG Sentences›
text ‹The quintuple-style of RG sentences.›
abbreviation rg_quint ::
"'a set ⇒ 'a rel ⇒ 'a com ⇒ 'a rel ⇒ 'a set ⇒ bool"
("{_,_} _ {_,_}") where
"{P, R} C {G, Q} ≡ ⊢ C sat [P, R, G, Q]"
text ‹Quintuples with invariants.›
abbreviation rg_quint_invar ::
"'a set ⇒ 'a rel ⇒ 'a com ⇒ 'a set ⇒ 'a rel ⇒ 'a set ⇒ bool"
("{_,_} _ ⫽ _ {_,_}") where
"{P, R} C ⫽ I {G, Q} ≡ ⊢ C sat [
P ∩ I,
R ∩ pred_to_rel I,
invar_and_guar I G,
Q ∩ I]"
text ‹The keyword-style of RG sentences.›
abbreviation rg_keyword ::
"'a rel ⇒ 'a rel ⇒ 'a set ⇒ 'a com ⇒ 'a set ⇒ bool"
("rely:_ guar:_ code: {_} _ {_}") where
"rg_keyword R G P C Q ≡ ⊢ C sat [P, R, G, Q]"
text ‹Keyword-style RG sentences with invariants.›
abbreviation rg_keyword_invar ::
"'a rel ⇒ 'a rel ⇒ 'a set ⇒ 'a set ⇒ 'a com ⇒ 'a set ⇒ bool"
("rely:_ guar:_ inv:_ code: {_} _ {_}") where
"rg_keyword_invar R G I P C Q ≡ ⊢ C sat [
P ∩ I,
R ∩ pred_to_rel I,
invar_and_guar I G,
Q ∩ I]"
subsection ‹RG Subgoal-Generating Methods›
text ‹As in Floyd-Hoare logic, in RG we can strengthen (make smaller) the
precondition and weaken (make larger) the postcondition without affecting the
validity of an RG sentence.›
theorem strengthen_pre:
assumes "P' ⊆ P"
and "⊢ c sat [P, R, G, Q]"
shows "⊢ c sat [P', R, G, Q]"
using assms Conseq by blast
theorem weaken_post:
assumes "Q ⊆ Q'"
and "⊢ c sat [P, R, G, Q ]"
shows "⊢ c sat [P, R, G, Q']"
using assms Conseq by blast
text ‹We then develop subgoal-generating methods for various instruction
types and patterns, to be used in conjunction with the Isar proof-language.›
subsubsection ‹Basic›
text ‹A @{text Basic} instruction wraps a state-transformation function.›
theorem rg_basic_named[intro]:
assumes "stable P R"
and "stable Q R"
and "∀s. s ∈ P ⟶ (s, s) ∈ G"
and "∀s. s ∈ P ⟶ (s, f s) ∈ G"
and "P ⊆ ⦃ ´f ∈ Q ⦄"
shows "{P, R} Basic f {G, Q}"
using assms apply -
by (rule Basic; fastforce)
method method_basic =
rule rg_basic_named,
goal_cases stable_pre stable_post guar_id establish_guar establish_post
text ‹The \emph{skip} command is a @{text Basic} instruction whose function is
the identity.›
theorem rg_skip_named:
assumes "stable P R"
and "stable Q R"
and "Id ⊆ G"
and "P ⊆ Q"
shows "{P, R} SKIP {G, Q}"
using assms by force
method method_skip =
rule rg_skip_named,
goal_cases stab_pre stab_post guar_id est_post
text ‹An alternative version with an invariant subgoal.›
theorem rg_basic_inv[intro]:
assumes "stable (P ∩ I) (R ∩ pred_to_rel I)"
and "stable (Q ∩ I) (R ∩ pred_to_rel I)"
and "∀s. s ∈ P ∩ I ⟶ (s, s) ∈ G"
and "∀s. s ∈ P ∩ I ⟶ f s ∈ I"
and "∀s. s ∈ P ∩ I ⟶ f s ∈ Q"
and "∀s. s ∈ P ∩ I ⟶ (s, f s) ∈ G"
shows "⊢ (Basic f) sat [
P ∩ I,
R ∩ pred_to_rel I,
invar_and_guar I G,
Q ∩ I]"
using assms apply -
by (method_basic; fastforce)
method method_basic_inv = rule rg_basic_inv,
goal_cases stab_pre stab_post id_guar est_inv est_post est_guar
subsubsection ‹Looping constructs›
theorem rg_general_loop_named[intro]:
assumes "stable P R"
and "stable Q R"
and "Id ⊆ G"
and "P ∩ -b ⊆ Q"
and "{P ∩ b, R} c {G, P}"
shows "{P, R} While b c {G, Q}"
using assms apply -
by (rule While; fastforce)
method method_loop =
rule rg_general_loop_named,
goal_cases stable_pre stable_post id_guar loop_exit loop_body
text ‹A similar version but with the @{text loop_body} subgoal having a
weakend precondition.›
theorem rg_general_loop_no_guard[intro]:
assumes "stable P R"
and "stable Q R"
and "Id ⊆ G"
and "P ∩ -b ⊆ Q"
and "{P, R} c {G, P}"
shows "{P, R} While b c {G, Q}"
apply(rule rg_general_loop_named)
by (fastforce intro!: assms Int_lower1 intro: strengthen_pre)+
method method_loop_no_guard =
rule rg_general_loop_no_guard,
goal_cases stab_pre stab_post guar_id loop_exit loop_body
text ‹A \emph{spinloop} is a loop with an empty body. Such a loop repeatedly
checks a property, and is a key construct in mutual exclusion algorithms.›
theorem rg_spinloop_named[intro]:
assumes "stable P R"
and "stable Q R"
and "Id ⊆ G"
and "P ∩ -b ⊆ Q"
shows "{P, R} While b SKIP {G, Q}"
using assms
by (fastforce simp: rg_general_loop_no_guard rg_skip_named)
method method_spinloop =
rule rg_spinloop_named,
goal_cases stable_pre stable_post guar_id est_post
theorem rg_infinite_loop:
assumes "stable P R"
and "Id ⊆ G"
and "{P, R} C {G, P}"
shows "{P, R} While UNIV C {G, Q}"
proof -
have "{P, R} While UNIV C {G, {}}"
using assms by (fastforce simp: rg_general_loop_no_guard)
thus ?thesis
using weaken_post by fastforce
qed
method method_infinite_loop =
rule rg_infinite_loop,
goal_cases stable_pre guar_id loop_body,
clarsimp+
theorem rg_infinite_loop_syntax:
assumes "stable P R"
and "Id ⊆ G"
and "{P, R} C {G, P}"
shows "{P, R} WHILE True DO C OD {G, Q}"
using assms by (fastforce simp: rg_infinite_loop)
method method_infinite_loop_syntax =
rule rg_infinite_loop_syntax,
goal_cases stable_pre guar_id loop_body
text ‹A \emph{repeat-loop} encodes the pattern where the loop body is executed
before the first evaluation of the guard.›
theorem rg_repeat_loop[intro]:
assumes "stable P R"
and "stable Q R"
and "Id ⊆ G"
and "P ∩ b ⊆ Q"
and loop_body: "{P, R} C {G, P}"
shows "{P, R} C ;; While (-b) C {G, Q}"
using assms apply -
apply (rule Seq)
apply (force intro: loop_body)
by (method_loop_no_guard; fastforce)
method method_repeat_loop =
rule rg_repeat_loop,
goal_cases stab_pre stab_post guar_id loop_exit loop_body
text ‹When reasoning about repeat-loops, we may need information from @{text P}
to determine whether we reach the postcondition. In this case we can use the
following form, which introduces a mid-state.›
theorem rg_repeat_loop_mid[intro]:
assumes stab_pre: "stable (P ∩ M) R"
and stab_post: "stable Q R"
and guar_id: "Id ⊆ G"
and loop_exit: "P ∩ M ∩ b ⊆ Q"
and loop_body: "{P, R} C {G, P ∩ M}"
shows "{P, R} C ;; While (-b) C {G, Q}"
using assms apply -
apply (rule Seq)
apply (fast intro: loop_body)
by (method_loop_no_guard; fast intro: loop_body strengthen_pre)
method method_repeat_loop_mid =
rule rg_repeat_loop_mid,
goal_cases stab_pre stab_post guar_id loop_exit loop_body
text ‹We define dedicated syntax for the repeat-loop pattern.›
definition Repeat :: "'a com ⇒ 'a bexp ⇒ 'a com" where
"Repeat c b ≡ c ;; While (-b) c"
syntax "_Repeat" :: "'a com ⇒ 'a bexp ⇒ 'a com" ("(0REPEAT _ /UNTIL _ /END)" [0, 0] 61)
translations "REPEAT c UNTIL b END" ⇀ "CONST Repeat c ⦃b⦄"
theorem rg_repeat_loop_def[intro]:
assumes stab_pre: "stable P R"
and stab_post: "stable Q R"
and guar_id: "Id ⊆ G"
and loop_exit: "P ∩ b ⊆ Q"
and loop_body: "{P, R} C {G, P}"
shows "{P, R} Repeat C b {G, Q}"
using assms
by (fastforce simp: Repeat_def rg_repeat_loop)
method method_repeat_loop_def =
rule rg_repeat_loop_def,
goal_cases stab_pre stab_post guar_id loop_exit loop_body
subsubsection ‹Conditionals›
text ‹We first cover conditional-statements with or without the else-branch.›
theorem rg_cond_named[intro]:
assumes stab_pre: "stable P R"
and stab_post: "stable Q R"
and guar_id: "Id ⊆ G"
and then_br: "{P ∩ b, R} c1 {G, Q}"
and else_br: "{P ∩ -b, R} c2 {G, Q}"
shows "{P, R} Cond b c1 c2 {G, Q}"
using assms apply -
by (rule Cond; fastforce)
theorem rg_cond2_named[intro]:
assumes stab_pre: "stable P R"
and stab_post: "stable Q R"
and guar_id: "Id ⊆ G"
and then_br: "{P ∩ b, R} c1 {G, Q}"
and else_br: "P ∩ -b ⊆ Q"
shows "{P, R} Cond b c1 SKIP {G, Q}"
using assms apply -
by (rule rg_cond_named; fastforce simp: rg_skip_named strengthen_pre)
method method_cond =
(rule rg_cond2_named | rule rg_cond_named),
goal_cases stab_pre stab_post guar_id then_br else_br
text ‹Variants without the stable-post subgoal.›
theorem rg_cond_no_post[intro]:
assumes stable_pre: "stable P R"
and guar_id: "Id ⊆ G"
and then_br: "{P ∩ b, R} c1 {G, Q}"
and else_br: "{P ∩ -b, R} c2 {G, Q}"
shows "{P, R} Cond b c1 c2 {G, Q}"
using assms by (fastforce simp: Cond subset_iff)
theorem rg_cond_no_guard_no_post[intro]:
assumes stable_pre: "stable P R"
and guar_id: "Id ⊆ G"
and then_br: "{P, R} c1 {G, Q}"
and else_br: "{P, R} c2 {G, Q}"
shows "{P, R} Cond b c1 c2 {G, Q}"
using assms apply -
by (rule Cond; fastforce intro: strengthen_pre)
method method_cond_no_post =
(rule rg_cond_no_post | rule rg_cond_no_guard_no_post),
goal_cases stab_pre guar_id then_br else_br
subsection ‹Parallel Compositions›
text ‹We now turn to the parallel composition, and cover several variants,
from the \emph{binary} parallel composition of two commands, to the
\emph{multi-parallel} composition of an indexed list of commands.
For each variant, we define the syntax and devise the subgoal-generating methods.›
subsubsection ‹Binary Parallel›
text ‹The syntax of binary parallel composition, without and with invariant.›
abbreviation binary_parallel ::
"'a set ⇒ 'a rel ⇒ 'a com ⇒ 'a com ⇒ 'a rel ⇒ 'a set ⇒ bool"
("{_, _} _ ∥ _ {_, _}") where
"{P, R} C1 ∥ C2 {G, Q} ≡
∃ P1 P2 R1 R2 G1 G2 Q1 Q2.
⊢ COBEGIN
(C1, P1, R1, G1, Q1)
∥
(C2, P2, R2, G2, Q2)
COEND SAT [P, R, G, Q]"
abbreviation binary_parallel_invar ::
"'a set ⇒ 'a rel ⇒ 'a com ⇒ 'a com ⇒ 'a set ⇒ 'a rel ⇒ 'a set ⇒ bool"
("{_, _} _ ∥ _ ⫽ _ {_, _}") where
"{P, R} C1 ∥ C2 ⫽ I {G, Q} ≡
∃ P1 P2 R1 R2 G1 G2 Q1 Q2.
⊢ COBEGIN
(C1, P1, R1, G1, Q1)
∥
(C2, P2, R2, G2, Q2)
COEND SAT [P ∩ I, R ∩ pred_to_rel I, invar_and_guar I G, Q ∩ I]"
text ‹Some helper lemmas for later.›
lemma simp_all_2:
"(∀ i < Suc (Suc 0). P i) ⟷ P 0 ∧ P 1"
by (fastforce simp: less_Suc_eq)
lemma simp_gen_Un_2:
"(⋃ x ∈ ⦃´(<) (Suc (Suc 0)) ⦄. S x) = S 0 ∪ S 1"
by (fastforce simp: less_Suc_eq)
lemma simp_gen_Un_2_not0:
"(⋃ x ∈ ⦃´(<) (Suc (Suc 0)) ∧ ´(≠) (Suc 0) ⦄. S x) = S 0"
by (fastforce simp: less_Suc_eq)
lemma simp_gen_Int_2:
"(⋂ x ∈ ⦃´(<) (Suc (Suc 0)) ⦄. S x) = S 0 ∩ S 1"
by (fastforce simp: less_Suc_eq)
theorem rg_binary_parallel:
assumes "{P1, R1} (C1::'a com) {G1, Q1}"
and "{P2, R2} (C2::'a com) {G2, Q2}"
and "G1 ⊆ R2"
and "G2 ⊆ R1"
and "P ⊆ P1 ∩ P2"
and "R ⊆ R1 ∩ R2"
and "G1 ∪ G2 ⊆ G"
and "Q1 ∩ Q2 ⊆ Q"
shows "⊢ COBEGIN
(C1, P1, R1, G1, Q1)
∥
(C2, P2, R2, G2, Q2)
COEND SAT [P, R, G, Q]"
using assms apply -
apply (rule Parallel)
by (simp_all add: simp_all_2 simp_gen_Un_2 simp_gen_Int_2 simp_gen_Un_2_not0)
theorem rg_binary_parallel_exists:
assumes "{P1, R1} (C1::'a com) {G1, Q1}"
and "{P2, R2} (C2::'a com) {G2, Q2}"
and "G1 ⊆ R2"
and "G2 ⊆ R1"
and "P ⊆ P1 ∩ P2"
and "R ⊆ R1 ∩ R2"
and "G1 ∪ G2 ⊆ G"
and "Q1 ∩ Q2 ⊆ Q"
shows "{P, R} C1 ∥ C2 {G, Q}"
by (metis assms rg_binary_parallel)
theorem rg_binary_parallel_invar_conseq:
assumes C1: "{P1, R1} (C1::'a com) ⫽ I {G1, Q1}"
and C2: "{P2, R2} (C2::'a com) ⫽ I {G2, Q2}"
and "G1 ⊆ R2"
and "G2 ⊆ R1"
and "P ⊆ P1 ∩ P2"
and "R ⊆ R1 ∩ R2"
and "Q1 ∩ Q2 ⊆ Q"
and "G1 ∪ G2 ⊆ G"
shows "{P, R} C1 ∥ C2 ⫽ I {G, Q}"
using assms apply -
apply (rule rg_binary_parallel_exists)
by force+
subsubsection ‹Multi-Parallel›
text ‹The syntax of multi-parallel, without and with invariants.›
syntax multi_parallel ::
"'a set ⇒ 'a rel ⇒ idt ⇒ nat ⇒
(nat ⇒ 'a set) ⇒ (nat ⇒ 'a rel) ⇒
(nat ⇒ 'a com) ⇒
(nat ⇒ 'a rel) ⇒ (nat ⇒ 'a set) ⇒
'a rel ⇒ 'a set ⇒ bool"
("global'_init: _ global'_rely: _ ∥ _ < _ @ {_,_} _ {_,_} global'_guar: _ global'_post: _")
translations
"global_init: Init global_rely: RR ∥ i < N @
{P,R} c {G,Q} global_guar: GG global_post: QQ"
⇀ "⊢ COBEGIN SCHEME [0 ≤ i < N] (c, P, R, G, Q) COEND
SAT [Init, RR , GG, QQ]"
syntax multi_parallel_inv ::
"'a set ⇒ 'a rel ⇒ idt ⇒ nat ⇒
(nat ⇒ 'a set) ⇒ (nat ⇒ 'a rel) ⇒
(nat ⇒ 'a com) ⇒ (nat ⇒ 'a set) ⇒
(nat ⇒ 'a rel) ⇒ (nat ⇒ 'a set) ⇒
'a rel ⇒ 'a set ⇒ bool"
("global'_init: _ global'_rely: _ ∥ _ < _ @ {_,_} _ ⫽ _ {_,_} global'_guar: _ global'_post: _")
translations
"global_init: Init global_rely: RR ∥ i < N @
{P, R} c ⫽ I {G, Q} global_guar: GG global_post: QQ"
⇀ "⊢ COBEGIN SCHEME [0 ≤ i < N] (c,
P ∩ I,
R ∩ CONST pred_to_rel I,
CONST invar_and_guar I G,
Q ∩ I
) COEND
SAT [Init, RR , GG, QQ]"
text ‹The subgoal-generating method for multi-parallel.›
theorem rg_multi_parallel_subgoals:
assumes assm_guar_rely: "∀ i j. i ≠ j ⟶ i < N ⟶ j < N ⟶ G j ⊆ R i"
and assm_pre: "∀ i < N. P' ⊆ P i"
and assm_rely: "∀ i < N. R' ⊆ R i"
and assm_guar: "∀ i < N. G i ⊆ G'"
and assm_post: "(⋂ i ∈ { i. i < N }. Q i) ⊆ Q'"
and assm_local: "∀ i<N. ⊢ C i sat [P i, R i, G i, Q i]"
shows "⊢ COBEGIN SCHEME [0 ≤ i < (N::nat)]
(C i, P i, R i, G i, Q i)
COEND SAT [P', R', G', Q']"
proof (rule Parallel, goal_cases)
case 1 show ?case using assm_rely assm_guar_rely by (simp add: SUP_le_iff)
case 2 show ?case using assm_guar by force
case 3 show ?case using assm_pre by force
case 4 show ?case using assm_post by force
case 5 show ?case using assm_local by force
qed
method method_multi_parallel = rule rg_multi_parallel_subgoals,
goal_cases guar_rely pre rely guar post body
theorem rg_multi_parallel_nobound_subgoals:
assumes assm_guar_rely: "∀ i j. i ≠ j ⟶ G j ⊆ R i"
and assm_pre: "∀ i. P' ⊆ P i"
and assm_rely: "∀ i. R' ⊆ R i"
and assm_guar: "∀ i. G i ⊆ G'"
and assm_post: "(⋂ i ∈ { i. i < N }. Q i) ⊆ Q'"
and assm_local: "∀ i. ⊢ C i sat [P i, R i, G i, Q i]"
shows "⊢ COBEGIN SCHEME [0 ≤ i < (N::nat)]
(C i, P i, R i, G i, Q i)
COEND SAT [P', R', G', Q']"
using assms apply -
apply (rule Parallel)
by (simp_all add: SUP_le_iff INT_greatest)
method method_multi_parallel_nobound =
rule rg_multi_parallel_nobound_subgoals,
goal_cases guar_rely pre rely guar post body
subsection ‹Syntax of Record-Updates›
text ‹This section contains syntactic sugars for updating a field of a record.
As we use records to model the states of a program, these record-update
operations correspond to the variable-assignments.
The type @{typ idt} denotes a field of a record. The first syntactic sugar
expresses a Basic command (of type @{typ[cartouche=true] ‹'a com›}) that
updates a record-field @{term x} that is a function; often @{term x} models
an array. After the update, the new value of @{term[cartouche=true] ‹x i›}
becomes @{term a}.›
syntax "_record_array_assign" ::
"idt ⇒ 'index ⇒ 'expr ⇒ 'state com" ("(´_[_] :=/ _)" [70, 65, 64] 61)
translations "´x[i] := a"
⇀ "CONST Basic «´(_update_name x (λ_. ´x(i:= a)))»"
text ‹The next two syntactic sugars express a state-transformation
function (rather than a command) that updates record-fields.
The first one simply updates an entire variable @{term x},
while the second updates an array @{term[cartouche=true] ‹x i›}.›
syntax "_record_update_field" ::
"idt ⇒ 'expr ⇒ ('a ⇒ 'a)" ("´_ ←/ _" [70] 61)
translations "´x ← a"
⇌ "«´(_update_name x (λ_. a))»"
syntax "_record_update_array" ::
"idt ⇒ 'expr ⇒ 'expr ⇒ ('a ⇒ 'a)" ("´_[_] ←/ _" [70, 71] 61)
translations "´x[i] ← a"
⇀ "«´(_update_name x (λ_. ´x(i:= a)))»"
text ‹Syntactic sugars for incrementing variables.›
syntax "_inc_fn" :: "idt ⇒ 'c ⇒ 'c" ("(´_.++)" 61)
translations "´x.++ " ⇀
" «´(_update_name x (λ_. ´x + 1))»"
syntax "_inc" :: "idt ⇒ 'c com" ("(´_++)" 61)
translations "´x++ " ⇀
"CONST Basic (´x.++)"
end