명세의 뜻은 새로 만들 것이 없다. 2장의 ⟦c⟧ : State V → Flat (State V)가 이미 있으므로,
그 함수가 단언 둘 사이에서 어떻게 행동하는지 말하면 된다. 알맹이를 상태 변환기 w 하나에
대한 성질로 떼어 둔다.
/--
`w` 가 `P` 에서 출발해 끝나면 `Q` 다. 부분 정확성의 알맹이.
`w σ = ⊥` 면 조건 없이 참이다 — `⊥` 는 모든 사후조건을 만족한다.
-/defSat(P:StateV→Prop)(w:StateV→SigmaBotV)(Q:StateV→Prop):Prop:=∀σ,Pσ→∀τ,wσ=Flat.someτ→Qτ
w σ = Flat.none이면 조건이 공허하게 참이다. ⊥는 모든 사후조건을 만족한다. 사소해
보이지만 이 장 전체를 떠받치는 사실이다. while의 뜻은 ⊥에서 출발한 근사들의 극한이었고
(§2.4), 그 출발점이 어떤 명세든 만족하므로 귀납이 시작될 수 있다.
Scott 귀납법(§2.4)은 성질이 허용 가능할 것을 요구한다. 사슬의 모든 항이 만족하면
극한도 만족해야 한다. Sat P · Q가 그렇다.
omit[DecidableEqV]in/--
**사후조건 만족은 극한을 통과한다.** 상태 `σ` 하나를 고정한 판.
`d.lub σ` 는 `Σ⊥` 의 사슬 `d.apply σ` 의 극한이고, 평평한 사슬의 극한은 어느 항
`d.seq k σ` 와 같다 (`Chain.flat_lub_mem_range`). 그 항에서 가정이 `Q` 를 준다.
이것이 §2.4 에서 말한 **허용 가능성**이다. §2.5 의 `AgreeOn.admissible` 과 같은 논증인데
관계가 아니라 술어라 더 짧다.
-/theoremsat_admissible(Q:StateV→Prop)(σ:StateV)(d:Chain(StateV→SigmaBotV))(h:∀nτ,d.seqnσ=Flat.someτ→Qτ):∀τ,d.lubσ=Flat.someτ→Qτ:=V:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qτ⊢ ∀(τ:StateV),d.lubσ=Flat.someτ→QτV:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qττ:StateVhτ:d.lubσ=Flat.someτ⊢ QτV:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qττ:StateVhτ:(d.applyσ).lub=Flat.someτ⊢ Qτobtain⟨k,hk⟩:=(d.applyσ).flat_lub_mem_rangeV:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qττ:StateVhτ:(d.applyσ).lub=Flat.someτk:ℕhk:(d.applyσ).seqk=(d.applyσ).lub⊢ Qτrw[←hkV:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qττ:StateVk:ℕhτ:(d.applyσ).seqk=Flat.someτhk:(d.applyσ).seqk=(d.applyσ).lub⊢ Qτ]athτV:Type uQ:StateV→Propσ:StateVd:Chain(StateV→SigmaBotV)h:∀(n:ℕ)(τ:StateV),d.seqnσ=Flat.someτ→Qττ:StateVk:ℕhτ:(d.applyσ).seqk=Flat.someτhk:(d.applyσ).seqk=(d.applyσ).lub⊢ QτexacthkτhτAll goals completed! 🐙
증명의 요점은 §2.3의 평평한 순서다. Flat (State V)의 사슬은 Flat.none에서 Flat.some τ 하나로
한 번 올라가고 멈춘다. 그래서 극한은 어느 항과 같고, 그 항에서 이미 성질이 성립한다.
전체 정확성도 사슬의 극한을 통과한다. 그러나 사전조건을 만족하는 상태가 있으면
Flat.none으로만 이루어진 사슬의 첫 항 ⊥는 전체 정확성을 만족하지 않는다. Scott 귀납법의
시작 조건이 성립하지 않으므로, 전체 정확성의 while 규칙은 종료까지 보이는 측도를
사용해 정초 귀납으로 증명한다(§3.4).
이 두 주장을 하나의 연습으로 확인한다. 첫 성분은 모든 사슬 항의 종료 증인에서
극한의 종료 증인을 얻는다. 둘째 성분의 ∃ σ, P σ는 필요하다. 사전조건을 만족하는
상태가 없으면 바닥 함수에서도 전체 정확성이 공허하게 참이기 때문이다.
omit[DecidableEqV]in/--
전체 정확성도 사슬의 극한에서 보존된다. 그러나 사전조건을 만족하는 상태가 있으면
바닥 함수는 종료 결과를 줄 수 없다. 따라서 이 극한 보존만으로 Scott 귀납법을 쓸 수 없다.
**책과의 차이**: §3.1의 명세 의미를 평평한 상태 변환기에 적용하는 보충 연습이다.
책이 명시한 전체 정확성의 위쪽 닫힘(`m' ⊒ m` 이면 `m` 에서 성립한 명세가 `m'` 에서도
성립함, p.56)은 여기 첫 연언의 특수 경우라 따로 정리로 두지 않는다 — 두면 이 연습의
첫 연언이 자명해진다(연습 독립성 원칙). 대신 극한 보존과 바닥에서의 실패를 함께 확인한다.
-/@[exercise"§3.1 total-admissible"2]theoremtotal_admissible(PQ:StateV→Prop):(∀d:Chain(StateV→SigmaBotV),(∀nσ,Pσ→∃τ,d.seqnσ=Flat.someτ∧Qτ)→∀σ,Pσ→∃τ,d.lubσ=Flat.someτ∧Qτ)∧((∃σ,Pσ)→¬(∀σ,Pσ→∃τ,(⊥:StateV→SigmaBotV)σ=Flat.someτ∧Qτ))
단언을 구문(Assert)으로 둘지 의미(State V → Prop)로 둘지는 두 판을 다 두는 것으로
답한다.
/-- 의미 판 부분 정확성. 단언이 상태 위의 술어다. -/defPartialCorrectS(P:StateV→Prop)(c:CommV)(Q:StateV→Prop):Prop:=SatP⟦c⟧ᶜQ/-- 의미 판 전체 정확성. 끝나야 하고, 끝난 상태가 `Q` 다. -/defTotalCorrectS(P:StateV→Prop)(c:CommV)(Q:StateV→Prop):Prop:=∀σ,Pσ→∃τ,⟦c⟧ᶜσ=Flat.someτ∧Qτ/-- **부분 정확성** `{ p } c { q }`. Reynolds §3.1. 발산하면 공허하게 참. -/defPartialCorrect(p:AssertV)(c:CommV)(q:AssertV):Prop:=PartialCorrectS⟦p⟧ₐc⟦q⟧ₐ/-- **전체 정확성** `[ p ] c [ q ]`. Reynolds §3.1. 반드시 끝나고 `q` 다. -/defTotalCorrect(p:AssertV)(c:CommV)(q:AssertV):Prop:=TotalCorrectS⟦p⟧ₐc⟦q⟧ₐ
구문 판
추론 규칙이 여기에 선다. 대입 공리가 단언에 치환을 걸어야 하므로 단언이 구문이어야
한다.
의미 판
불변식에 fib나 거듭제곱이 드는 예제(§3.6, §3.7)가 여기에 선다. Assert로는 그런
술어를 적을 수 없다(적으려면 괴델 부호화가 필요하다 — 보충 Wlp.lean 참고).
구문 판은 ⟦p⟧ₐ를 거쳐 의미 판의 특수 경우가 된다. 둘을 잇는 데 정리가 필요 없다.
힌트: 두 정의를 펼치면 (intro σ hp τ hτ) 전체 정확성이 준 종료 증인과 가정의 τ가
같은 Flat.some의 안이다. Flat.some.inj로 둘을 같게 만든다.
그리고 둘의 차이는 정확히 종료다.
/--
**전체 정확성 = 부분 정확성 + 종료.** 비종료를 어느 쪽으로 세느냐가 두 명세의 유일한
차이라는 것을 한 등식으로 적은 것이다.
-/@[exercise"§3.1 halts-iff"1]theoremtotalCorrect_iff_partial_halts{pq:AssertV}{c:CommV}:[p]c[q]↔{p}c{q}∧Haltspc
힌트: →는 전체 정확성이 준 종료 상태로 두 성분을 각각 만든다. ←는
Flat.isSome_iff_exists로 종료 상태를 꺼낸 뒤 부분 정확성에 넣는다.
두 정리 모두 정의를 펼치면 나오지만, 한 가지를 짚어 둔다. Flat.some τ' = Flat.some τ에서
τ' = τ를 얻는 것이 결정적 의미의 성질이라는 점이다. 2장의 명령은 결정적이라 끝나면
결과가 하나뿐이다. 비결정적 언어(7장)에서는 이 등식이 깨지고, 부분·전체 정확성의 관계도
다시 따져야 한다.
⊤를 Flat.some (), ⊥를 Flat.none으로 두면 Flat Unit이 그 도메인이고, ⊑는
함의다. 책의 강한 확장 f⊥⊥는 Flat.bind다(§2.3 끝).
openClassicalin/--
**성공 램프(success lamp) `pt`.** 참이면 ⊤(`Flat.some ()`), 거짓이면 ⊥(`Flat.none`).
Reynolds §3.1 p.56. 결정 가능성은 `Prop`에 일반 `Decidable` 인스턴스가 없으므로
classical(`Classical.propDecidable`)로 얻는다 — `open Classical in` 이 그 인스턴스를 연다.
-/noncomputabledefpt(P:Prop):FlatUnit:=ifPthen.some()else.noneopenClassicalin/-- **위반 경보(violation alarm) `pf`.** 거짓이면 ⊤, 참이면 ⊥ — `pt (¬P)` 와 같은 값이다. -/noncomputabledefpf(P:Prop):FlatUnit:=ifPthen.noneelse.some()/--
**식 (3.1) — 부분 정확성의 순서 판.** 결과의 위반 경보가 사전조건의 위반 경보보다
정보가 적어야(⊑) 한다. `c` 가 발산하면 `Flat.bind` 가 바닥을 내므로 조건 없이 성립한다.
-/theorempartialCorrect_iff_pf(pq:AssertV)(c:CommV):{p}c{q}↔(funσ=>Flat.bind(⟦c⟧ᶜσ)(funτ=>pf(⟦q⟧ₐτ)))≤(funσ=>pf(⟦p⟧ₐσ))/--
**식 (3.2) — 전체 정확성의 순서 판.** 사전조건의 성공 램프가 결과의 성공 램프보다
정보가 적어야 한다. `c` 가 발산하면 결과가 ⊥(`Flat.bind` 의 바닥)라 사전조건이 참일 때
만족할 수 없다 — 그래서 종료까지 요구한다.
-/theoremtotalCorrect_iff_pt(pq:AssertV)(c:CommV):[p]c[q]↔(funσ=>pt(⟦p⟧ₐσ))≤(funσ=>Flat.bind(⟦c⟧ᶜσ)(funτ=>pt(⟦q⟧ₐτ)))
두 등가는 정의를 펼치고 Flat Unit의 두 원소(none, some ())로 나누면 나온다 — 증명은
Spec.lean에 있다. 전체 정확성 쪽은 c가 발산하면 Flat.bind도 바닥이라 사전조건이
참일 때 성립할 수 없다는 것이 핵심이다. 부분 정확성의 아래로 닫힘(m' ⊑ m이면
m에서 성립한 명세가 m'에서도 성립함)은 Sat.of_le가 준다.