Lowent 매뉴얼←↑→

39 수학 도구상자

먼저 알아야 할 것

38장 왜 증명하는가 · 시험은 있음(∃)을, 증명은 없음(∀)을 보인다
4장 수 · 폭과 부호가 타입을 정하고, 넓히기는 값을 지킬 때만 저절로 일어난다
7장 흐름 · while 은 조건이 참인 동안 몸을 되풀이한다

돌아보기

38장에서 증명하는 세 가지 방법은 무엇이었는가?

답. 불변식 보존(한 걸음이 참을 참으로 넘긴다), 순서 구조로 환원(문제를 순서의 성질로 바꾼다), 전수 검사(정해진 크기의 모든 경우를 돌린다 — 증명은 아니다)였다. 앞의 둘에는 수학의 어휘가 필요하다. 이 장이 그 어휘를 처음부터 모은다.

이 장의 필요성과 맥락

제10부의 나머지 장은 부분 순서, 격자, 고정점, 귀납, 추상해석이라는 낱말을 설명 없이 쓴다. 그 낱말들은 어려워 보이지만 뜻은 대부분 고등학교 수학이다 — 집합, 부등식, 도미노처럼 넘어가는 귀납. 이 장은 여섯 가지 도구를 Lowent 의 실제 프로그램에 붙여 한 번씩 보여 준다. 여기서 배운 기호는 뒤의 모든 장에서 그대로 쓰인다. 무엇을 증명한 장이 아니라 읽을 준비를 하는 장이다.

이 장이 끝나면

집합과 관계, 비교할 수 없는 쌍을 허락하는 부분 순서와 그 세 조건, 두 값을 합칠 때 고르는 join 과 격자를 알게 된다. 단조 함수와 고정점이 반복 분석을 끝나게 한다는 것, 귀납법과 불변식이 “몇 걸음이든” 을 보인다는 것, 값 대신 범위로 계산하는 추상해석과 그 건전성의 방향을 익힌다. 기호 표와, 여섯 도구가 뒤의 장에서 어떻게 맞물리는지도 보게 된다.

이 장에서 답할 질문

  1. 이 장에서 증명된 것은 무엇인가?

39.1 여섯 가지 도구#

도구한 줄어디서 쓰나
집합과 관계무엇들이 있고, 무엇이 무엇과 짝인가전부
부분 순서크기 비교인데 비교할 수 없는 쌍이 허락되는 순서40·46장
격자와 join두 개를 합칠 때 둘을 다 담는 가장 작은 것40·44장
단조 함수와 고정점더 이상 변하지 않는 점 — 반복 분석이 여기서 멈춘다41·43장
귀납법과 불변식첫 조각과 넘기는 규칙이 있으면 모든 조각이 넘어간다42·43장
추상해석값 대신 값의 범위로 계산한다41장

표 39.1 — 뒤의 장에서 쓰는 수학

39.2 집합과 관계#

집합은 모음이다 — {0, 1, 2}. 관계는 짝의 목록이다. “작다” 관계는 {(0,1), (0,2), (1,2)} 처럼 순서쌍의 모음이다. 프로그램 이야기에서는 타입의 집합 {u8, u16, u32, u64, i8, …} 위에 “안전하게 넓혀진다” 관계를 둔다. 그 관계를 ⊑ 로 적고 “왼쪽이 오른쪽에 값이 변하지 않고 들어간다” 로 읽는다. 부등호 ≤ 와 닮았지만 수의 크기가 아니라 담을 수 있는가를 말한다.

39.3 부분 순서 — 비교할 수 없는 쌍#

수는 언제나 비교된다. 3 과 5 가운데 하나는 크다. 타입은 그렇지 않다. u8 ⊑ u16 ⊑ u32 ⊑ u64 이고 i8 ⊑ i16 ⊑ i32 ⊑ i64 이지만, u32 와 i32 는 어느 쪽도 상대를 다 담지 못한다. u32 는 40억까지 담고 i32 는 음수를 담는다. 어느 쪽이 크다고 말할 수 없다는 사실을 정직하게 담는 구조가 부분 순서다.

조건뜻예
반사성자기 자신에는 늘 들어간다u8 ⊑ u8
추이성징검다리로 이어진다u8 ⊑ u16 이고 u16 ⊑ u32 이면 u8 ⊑ u32
반대칭성서로 들어가면 같은 것이다A ⊑ B 이고 B ⊑ A 이면 A = B

표 39.2 — 부분 순서의 세 조건

이 셋이 증명되어야 “안전한 넓히기” 라는 말이 뜻을 갖는다. 추이성이 깨지면 u8 → u16 과 u16 → u32 는 허락하면서 u8 → u32 는 막는 언어가 된다. 사용자는 왜 안 되는지 영영 알 수 없다. 이 셋은 40장에서 실제 타입에 대해 증명된다.

39.4 격자와 join — 합칠 때 무엇을 고르나#

두 값을 한 연산에 넣으면 처리기는 둘을 다 담는 타입에서 계산해야 한다. 후보는 여럿이지만 가장 작은 것을 고르는 것이 맞다. 그 “가장 작은 공통 상위” 를 join 이라 하고 ⊔ 로 적는다. 임의의 두 원소가 늘 join 을 갖는 부분 순서를 격자라 한다.

examples/ch39/wider.low

module widening .
rem run: wider 200 60000
rem trap: wider 255 65535

rem u8 과 u16 을 더하면 둘을 다 담는 가장 작은 타입(u16)에서 계산된다
fn wider input a u8 . input b u16 . output u32 . do
  return add a b .
end

실행 결과

$ lowentc --run wider wider.low 200 60000
wider(200, 60000) = 60200
$ lowentc --run wider wider.low 255 65535
== ir diagnostics (1) ==
0:0 E-VM-OVERFLOW: integer overflow at the declared width (use wrap_*/sat_*, or prove the range)

u8 ⊔ u16 = u16 이다. 그래서 출력 타입이 u32 여도 덧셈은 u16 에서 일어나고, 255 + 65535 는 u16 을 넘쳐 멈춘다. join 은 피연산자에서 정해지지, 결과를 받을 자리에서 정해지지 않는다.

부호가 섞이면 직관과 달라진다. u8 ⊔ i8 은 i8 이 아니라 i16 이다 — u8 의 255 와 i8 의 −128 을 함께 담으려면 폭이 더 넓어야 한다. 사람은 이런 자리에서 틀린다. Lowent 는 이 넓히기를 대신 고르지 않고 적게 한다.

examples/ch39/sign_join.low

module sign_join .
rem expect: E-TYPE-SIGN

rem u8 과 i8 을 둘 다 담는 타입은 i16 이다. 처리기는 그 넓히기를 대신 고르지 않는다
fn mix input a u8 . input b i8 . output i16 . do
  return add a b .
end

실행 결과

$ lowentc --check sign_join.low
sign_join.low:6:0 E-TYPE-SIGN: sign mismatch: no value-preserving widening exists (widen both to a strictly wider signed type, or bitcast_sign)

진단이 말하는 “strictly wider signed type” 이 바로 u8 ⊔ i8 = i16 이다. 둘을 모두 i16 으로 넓혀 적으면 받아들여진다.

39.5 단조 함수와 고정점 — 분석은 왜 멈추는가#

단조 함수는 입력이 커지면 출력이 작아지지 않는 함수다. 고정점은 f(x) = x 인 점, 곧 한 번 더 적용해도 변하지 않는 점이다.

처리기가 반복을 분석할 때 이 두 개념이 일한다. 분석기는 반복에 들어올 때 변수의 범위를 모른다. 그래서 좁게 시작해 되풀이하며 넓힌다.

1 바퀴 뒤: i ∈ [0, 0]
2 바퀴 뒤: i ∈ [0, 1]
3 바퀴 뒤: i ∈ [0, 2]      … 이대로면 끝나지 않는다

그래서 위드닝(widening)을 쓴다. 몇 번 넓어지는 것을 보면 “계속 커지겠다” 고 보고 한 번에 위 끝까지 민다. i ∈ [0, ∞) 에 반복 조건 i < 10 을 합치면 i ∈ [0, 9] 이고, 한 바퀴 더 돌려도 [0, 9] 다. 고정점에 닿았고 분석이 끝난다.

examples/ch39/count_up.low

module count_up .
rem run: ten
rem proof

fn ten output u8 . do
  var i u8 be 0 .
  var s u8 be 0 .
  while lt i 10 . do
    set s (add s i) .
    set i (add i 1) .
  end
  return s .
end

실행 결과

$ lowentc --run ten count_up.low
ten() = 45
$ lowentc --emit-proof count_up.low
# proven 1 certified 1
ten 14 add.i64 R-ARITH-RANGE 0 9 1 1 8 0

증명 줄이 하나다. add i 1 은 i ∈ [0, 9] 에서 더하므로 u8 을 넘지 않는다고 증명되었다(R-ARITH-RANGE 0 9). add s i 는 증명되지 않았다. 위드닝이 s 의 위 끝을 버렸기 때문이다. 실제로 s 는 45 를 넘지 않지만 분석은 그것을 모른다. 그래서 검사가 남는다 — 느릴 뿐 틀리지는 않는다.

39.6 귀납법과 불변식 — 도미노#

수학적 귀납법은 이렇다. P(0) 이 참이고, P(n) 이 참이면 P(n+1) 도 참이다. 그러면 모든 n 에 대해 P(n) 이 참이다. 프로그램에서는 n 이 “실행한 걸음 수” 가 되고, P 를 불변식 — 실행 내내 참인 성질 — 이라 부른다.

수학. 소유와 차용 정리의 모양

시작할 때 빌림 상태는 깨끗하다(P(0)). 정적 검사를 통과한 사건 하나를 실행하면 깨끗한 상태는 여전히 깨끗하다(P(n) → P(n+1)). 따라서 몇 걸음을 실행해도 차용 위반이 없다(∀n, P(n)). 이 논증의 좋은 점은 몇 걸음인지 몰라도 된다는 것이다. 그래서 반복을 몇 번 도는지 몰라도 결론이 선다(42·43장).

39.7 추상해석 — 값 대신 범위로#

프로그램을 돌리지 않고 값에 대해 무언가 알아내려면 값 하나 대신 값의 집합을 다뤄야 한다. 집합을 그대로 다루면 너무 크니 간단한 근사를 쓴다. 그 대표가 구간 [lo, hi] 다.

x ∈ [0, 5],  y ∈ [10, 20]  이면
x + y ∈ [10, 25]      양 끝끼리 더한다
x × y ∈ [0, 100]      부호가 섞이면 네 곱의 최소·최대를 본다

규칙은 하나다. 추상 계산의 결과는 실제로 가능한 모든 값을 포함해야 한다. 실제로 x 가 늘 3 인데 분석이 [0, 5] 라고 답하면 손해는 있어도 안전하다(검사를 못 지운다). 실제로 x 가 7 일 수 있는데 [0, 5] 라고 답하면, 그 답으로 검사를 지운 순간 범위 밖 접근이 일어난다. 그래서 분석의 모든 규칙은 “넓게” 쪽으로 기울어 있다.

흔한 오해. 분석이 정확할수록 좋다

정확함보다 먼저 오는 것이 방향이다. 넓게 틀리는 분석은 느릴 뿐이지만 좁게 틀리는 분석은 틀린 메모리를 읽는다. 정밀도를 높이는 규칙을 더할 때마다 물어야 하는 것은 “더 정확한가” 가 아니라 “실제보다 좁게 잡는 경우가 생기는가” 다. 그래서 이 언어는 무거운 분석 대신 넓게 틀리는 가벼운 분석을 두고, 모자란 자리에서는 검사를 남긴다.

39.8 기호 표#

기호읽는 법뜻
A ⊑ BA 는 B 에 안전하게 들어간다안전한 넓히기 관계
A ⊔ BA 와 B 의 join둘을 다 담는 가장 작은 것
[lo, hi]구간lo 이상 hi 이하의 값
∀x, P(x)모든 x 에 대해 P예외가 없다
∃x, P(x)어떤 x 가 있어 P하나라도 있다
P → QP 이면 Q함의
⊤모름아무 값이나 가능
A ∗ BA 와 B, 서로 다른 조각분리논리의 접속사(47장)

표 39.3 — 이 부의 기호

39.9 도구들이 맞물리는 방식#

부분 순서 ⊑           "안전하게 담긴다" 를 정의한다        (수)
   │
   ├─ join ⊔          합칠 때 타입을 고른다                (수 · 효과)
   │
격자 위의 단조 함수   반복 분석이 그 위를 오른다           (경계)
   │
고정점                분석이 멈추는 자리                   (경계 · 되풀이)
   │
추상해석의 건전성     "넓게" 틀리면 결론이 안전하다        (경계)
   │
불변식과 귀납법       실행 내내 유지됨을 보인다            (소유 · 되풀이 · 경합)

아래에서 위로 읽어도 된다. 귀납법으로 불변식을 지키고, 그 불변식이 추상해석의 건전성에서 오고, 추상해석은 격자 위에서 돌고, 격자는 부분 순서 위에 선다. 도구가 없으면 설계가 어림짐작이 된다 — join 을 손으로 적은 표는 u8 ⊔ i8 = i8 같은 줄을 섞어 255 를 −1 로 바꾸고, 위드닝 없는 반복 분석은 끝나지 않으며, 불변식 없는 차용 검사는 “이때는 되고 저때는 안 되는” 규칙 더미가 된다.

문. 이 장에서 증명된 것은 무엇인가?

답. 없다. 이 장은 어휘를 소개했다. 반사성·추이성·반대칭성, join 의 건전성, 고정점의 존재가 Lowent 의 실제 정의에 대해 성립한다는 것이 40장부터의 내용이다. 그리고 추상해석의 건전성은 규칙마다 “왜 이것이 넓은가” 를 따로 확인해야 한다. 한 규칙이라도 좁게 잡으면 그 자리에서 샌다. 그래서 41장은 규칙 하나하나를 그렇게 따진다.

복습 정리

부분 순서는 비교할 수 없는 쌍을 허락하는 순서이고 반사·추이·반대칭을 지킨다. join 은 둘을 다 담는 가장 작은 것이며 피연산자에서 정해진다 (u8 ⊔ u16 = u16, u8 ⊔ i8 = i16). 반복 분석은 위드닝으로 고정점에 닿아 멈추고, 위드닝은 넓게 잡아 검사를 남길 뿐 틀리지 않는다. 귀납법과 불변식은 “몇 걸음이든” 을 보이고, 추상해석은 값 대신 범위로 계산하되 늘 실제를 포함하는 쪽으로 틀린다.