# 027 — 계산의 핵: 상태는 값 하나, 값은 몇 가지로 짓는다 작성일: 2026-10-13. 상태: 설계와 단계. [026](026-app-contract.md)이 얼릴 계약 안에서 계산의 언어를 다시 세운다. 잴 도구는 [140](../research/140-spec-suite.md)의 명세 묶음이다. 1.0 전이라 호환은 챙기지 않는다. 사용자의 말: "진행해" (2026-10-13). 1단계를 만들었다([140](../research/140-spec-suite.md)의 뒷절). - aup로 쓴 SHA-256이 공식 벡터를 세 실행 길에서 모두 지난다. - 줄 수는 막대에 못 미쳤다(aup 52 대 파이썬 28). 남은 차이는 형식 지정, 바이트 잇기와 자르기, 값으로서의 리스트다. 이것들을 2단계에 더했다. - 시간은 C의 176배, 순수 파이썬의 0.43배다. ## 쉬운 말로 - **지금의 꼴.** aup와 Pure는 패턴마다 종류와 명령을 둔다. 커서, 인덱스, 문서, 표 보기, 메시지의 행이 각각 따로다. 그래서 세상의 명세를 담지 못하고(140), 새 패턴마다 형식과 세 실행 길이 함께 자란다. - **이 설계의 꼴.** 그것을 "값 몇 가지와, 그 값들이 서로를 담는 법"으로 바꾼다. - 상태는 타입 있는 값 하나다. - 값은 수(정수, 실수, bool), 바이트(text는 UTF-8인 바이트), 레코드, 갈래, 리스트(키가 있으면 맵)다. - **계산.** 함수(재귀 포함)와 접기다. 끝남은 문법이 아니라 예산이 보증한다. 이더리움의 가스와 같은 생각이다. 가스가 다하면 그 호출의 상태 변경이 모두 되돌아가는데, 이것이 026의 원자성과 같다. - **빠름.** 뜻이 아니라 구현에서 온다. 열로 담기, B+트리, 묶음 실행, 같은 답일 때만 차례 바꾸기가 그 수단이다. - **단계.** 다섯이다. - 1단계(바이트, 비트, 수의 리스트)로 SHA-256을 쓴다. - 2단계(중첩, 갈래, 재귀, 글자)로 JSON을 쓴다. - 3단계에서 중첩된 상태를 저장한다. - 4단계에서 패턴을 모은다. - 5단계에서 수의 모델을 정한다. - **근거.** - 140의 탐침 - 지금의 구현: 값의 칸 `Slot`, 열로 담은 B+트리 `PureList`, aup의 타입 여덟 - 선행 기술: 아래 "선행" 절 - **다음 결정.** 1단계를 만든다. ## 핵 ### 값 | 값 | 뜻 | 지금 | 바뀌는 것 | |---|---|---|---| | `bool` | 참, 거짓 | 같음 | — | | `i64` | 정확한 정수, −2^63 … 2^63−1, 넘치면 `overflow` | 같음 | 비트 연산(1단계). 이름과 리터럴의 기본은 5단계 | | `number` | 유한한 binary64 | 같음 | 5단계(수의 모델) | | `bytes` | 바이트의 열, 불변 | 길이와 text 변환뿐 | 자리로 읽기, 자르기, 잇기, 짓기(1단계) | | `text` | UTF-8로 검사한 바이트 | 같은 표현 | 글자로 읽기는 그 위의 견해(2단계) | | 레코드 | 이름 있는 필드의 곱 | 필드는 수, text, bool, i64 | 필드에 아무 값(2단계) | | 갈래 | 태그 붙은 경우의 합 | 없음 | 새로. `match`가 모든 경우를 다룬다(2단계) | | `list of T [by k]` | 차례 있는 열. `by k`면 k의 차례이고 키마다 하나다 | 레코드만, 상태와 이벤트와 메시지의 행으로 따로 | 아무 T(1단계 수, 2단계 전부). 키 없는 리스트의 키는 자리다 | | 앵커 | 편집을 따라가는 위치 | 커서와 인덱스(상태의 종류 둘) | 하나로(4단계) | ### 계산 - **그대로 두는 것.** 식, `let`, `if`, `for`(세기와 걷기), `break`. - **함수.** - 자신을 부를 수 있다(2단계). - 인라인은 구현의 선택이다. 작은 fn은 지금처럼 인라인하고, 재귀하는 fn은 부른다. - 매개변수에 타입을 적지 않는 것은 그대로다. 부르는 타입마다 한 벌을 만든다(단형화). - **`match`.** 갈래의 모든 경우를 다룬다. 빠진 경우는 컴파일 오류다(2단계). - **끝을 모르는 반복.** `while`을 둔다(2단계). 세기의 상한은 뜻에서 필요하지 않다. 묶음 실행이 시작할 때의 횟수를 쓰는 것은 구현이 상한을 알아볼 수 있을 때다. - **접기.** 뜻은 차례대로의 왼쪽 접기다. 구현은 같은 답과 같은 실패를 낼 때만 차례를 바꾼다. 묶음 실행기의 차례 없는 모으기가 이미 이 규칙을 따른다. - **비트 연산.** 정수를 무한한 2의 보수로 보고 정한다. 파이썬의 정수와 같은 뜻이다. - `&`, `|`, `^`, `~`: 비트마다의 연산이다. - `x << n`은 x·2^n이다. i64를 넘으면 `overflow`다. - `x >> n`은 floor(x / 2^n)이다. - n은 0부터 63까지다. 밖이면 `limit`이다. - 어느 기계에서나 같은 뜻이고, C의 정의되지 않은 동작이 없다. - **끝남과 결정성.** - 모든 걸음은 실행량을 쓰고, 부름의 깊이는 프레임의 바이트로 메모리 몫에 센다. 넘으면 `work`나 `memory`로 끝나고, 이벤트는 통째로 되돌아간다. - 결정성의 단위는 모듈이다. 같은 모듈과 같은 이벤트열이면 실패의 자리까지 같다. - 소스는 컴파일러의 판에 묶인다. EVM의 바이트코드와 가스표가 그렇다. 이것이 026의 의심 6(예산의 단위)에 대한 답이다. ### 효과 026 그대로다. 상태에 쓰기, 보내기, 답하기, `require`. ### 상태는 값 하나 - 앱의 상태는 레코드 값 하나다. 선언한 상태들이 그 필드이고, 수명(저장, transient)은 필드의 성질이다. - 저장은 바뀐 부분만 쓴다. 지금 리스트가 이미지와 로그로 하는 그대로다. 중첩된 리스트도 제 이미지와 로그를 가진다(경로로 이름을 붙인다, 3단계). - 값은 불변이라 스냅숏은 나누기만 한다. 지금 리스트의 공유 사본이 그 구현이다. ### 뜻과 표현 | 뜻 | 표현(구현이 고른다) | |---|---| | 레코드의 리스트 | 열로 담은 B+트리, 값만큼 좁힌 열, HASHED(지금) | | 수의 리스트 | 필드 하나인 레코드의 리스트(1단계), 나중에 배열 | | 바이트 짓기(`out = out + byte(x)`의 반복) | 왼쪽이 이벤트 공간의 끝에 있으면 제자리 덧붙이기 | | 표 보기 | 물을 때 칸 계산 | | 접기 | 묶음 실행, 차례 없는 모으기(같은 답일 때만) | | 작은 fn | 인라인 | ## 026의 의심에 대한 답 | 의심 | 답 | 단계 | |---|---|---| | 1. 입력의 종류 | 입력은 메시지 하나다. 키, 글자, 틱은 표면과 호스트가 만드는 메시지다 | 4 | | 2. 보기의 종류 | 보기는 아무 값을 낸다 | 4 | | 3. 커서와 인덱스 | 앵커 하나로 모은다. 다만 키가 있는 리스트에서 인덱스는 키와 같을 수 있다. 4단계에서 앵커가 text에만 필요한지 본다 | 4 | | 4. 키별 문서 | 없앤다. 키 있는 리스트가 한다 | 4 | | 5. 수의 모델 | 정할 것: 정수 리터럴의 기본, `/`와 `//`, 돈을 위한 십진수 | 5 | | 6. 예산의 단위 | 모듈로 정한다(위) | 이 문서 | | 7. 고정 한도 | 뜻에는 예산(실행량, 메모리)과 모듈의 크기만 남긴다. 상태 32, 입력 32, 레코드 16, 필드 16, 컬렉션 1, 중첩 8, aup 소스 32 KB는 뺀다 | 4 | | 8. 교체의 세부 | 수의 모델 뒤에 다시 본다 | 5 | | 9. 메시지의 행 | 메시지의 필드가 아무 값을 가지면 따로 둘 까닭이 없다 | 2 | ## 단계 ### 1단계: 바이트, 비트, 수의 리스트 (SHA-256) - **Pure.** - 바이트를 자리로 읽기(`b[i]` → i64, 밖이면 `limit`), 자르기, 잇기, 한 바이트로 짓기 - i64의 비트 연산 여섯 - 이벤트의 지역 칸이 바이트를 담는다 - **aup.** - `b[i]`, `b[a .. c]`, `b + c`, `byte(n)` - i64의 `& | ^ ~ << >>`, 16진 리터럴 `0x…` - `list of i64`와 `list of number`(안에서는 필드 하나인 레코드), 리스트 리터럴 `[ … ]` - 키 없는 리스트를 자리로 읽고 쓰기 `xs[i]` - **막대.** - aup로 쓴 SHA-256이 NIST의 짧은 메시지 벡터와 긴 메시지 벡터를 모두 지난다(Pure의 검사 안에서). - 줄 수가 같은 일을 하는 파이썬 참조 이하다. - C 대비 시간은 재고 기록하되 막대는 두지 않는다. 바이트와 비트의 일은 실행기로 돈다. 빠르게 하는 것은 핵이 선 뒤다. - 실행기만의 길에서 다섯 일(041)의 명령 수가 ±0.1%, 은행 질의의 명령 수가 ±1% 안이다. 새 명령은 기존 길을 바꾸지 않는다. - **검증.** 새 명령마다 경계(밖의 자리, 음수 시프트, 넘침)를 단위 시험으로 본다. 무작위 입력의 다이제스트는 파이썬 `hashlib`와 같아야 한다. ### 2단계: 중첩, 갈래, 재귀, 글자 (JSON) 크기 때문에 셋으로 나눈다(2026-10-13). 각각 재는 일이 있다. - **2a(만듦):** 형식 지정, 바이트 잇기와 자르기, 바이트와 text를 담는 `let`, `while`, `&&`와 `||`의 단락 평가. JSON을 받거나 거절하는 검사기로 잰다. 결과는 [140](../research/140-spec-suite.md)의 2a절이다. - JSONTestSuite에서 틀린 것이 없다. - 줄 수는 파이썬 이하다(80 대 90). - 시간은 C의 116배, 파이썬의 1.2배다. - 쓰다가 드러난 뜻의 틈 하나를 메웠다. `&&`가 양쪽을 다 계산해서 `i < n && d[i]`가 실패했다. 이제 단락 평가이고, 오른쪽이 실패할 수 없을 때는 지금처럼 AND 하나를 낸다. 그래서 TPC-C를 포함한 기존 모듈 35개가 바이트까지 같다. - **2b:** 중첩(레코드 안의 레코드와 리스트), 갈래와 `match`, 값으로서의 리스트, 메시지 필드의 아무 값. JSON을 트리로 읽고 묻는다. - **2c:** 재귀(부르기와 돌아가기, 함수마다의 검증), 글자로 읽기. - 레코드의 필드와 리스트의 원소가 아무 값을 가진다. - 갈래와 `match`를 둔다. - 재귀하는 fn과 `while`을 둔다. Pure에는 부르기와 돌아가기, 함수마다의 검증이 생긴다. - `for c in t`로 글자(코드 포인트)를 읽는다. - 메시지의 필드가 아무 값을 가진다. JSON의 배열과 객체가 곧 리스트와 레코드다. - 1단계의 SHA-256이 길었던 까닭을 여기서 푼다. - 형식 지정(`{x:08x}`, `{price:.2}`) - 바이트 값을 잇고 자르기(`b + c`, `b[a .. c]`)와 바이트를 담는 `let` - 값으로서의 리스트: 블록 안에서 짓고 다시 짓는다 - fn을 펼칠 때 리터럴 인자를 접기: 구현의 일이다. 140이 블록당 명령의 39%를 fn 호출의 몫으로 쟀다 - 먼저 이벤트와 보기의 값(메모리 안)에서 한다. 상태는 3단계다. - **막대.** - JSON 파서가 JSONTestSuite의 `y_`를 모두 받고 `n_`을 모두 거절한다. `i_`는 정한 쪽을 적는다. - 줄 수가 파이썬 참조 이하다. - 시간을 기록한다. - TPC-C와 은행 질의가 그대로다(026의 중단 기준). ### 3단계: 중첩된 상태의 저장 - 중첩된 리스트의 이미지와 로그를 경로로 이름 붙인다. - 스냅숏의 나누기와 교체의 이름 잇기가 깊이에서도 그대로 성립해야 한다. - **막대.** 메모리 앱과 수시로 재시작하는 저장 앱이 매 단계 비트까지 같다(지금의 대조를 중첩으로 넓힌다). 커밋의 모든 단계에서 죽여도 전부 아니면 없음이다. ### 4단계: 패턴 모으기 - 앵커, 메시지 하나인 입력, 아무 값인 보기를 둔다. - 키별 문서와 고정 한도를 없애고, 메시지의 행을 중첩으로 옮긴다. - **막대.** - Pure, aup, Vega의 모든 검사를 옮긴 뒤 초록이다. - 명령과 상태 종류의 수, 코어의 줄 수가 줄었다(세어 적는다). - TPC-C와 은행 질의가 그대로다. ### 5단계: 수의 모델 - 정수 리터럴의 기본, `/`와 `//`, 돈의 십진수, 묶음 실행기의 2^53 검사를 정한다. - **막대.** TPC-C가 SQLite와 행마다 같은 상태를 남기는 것이 그대로다. **순서의 까닭.** - 1과 2가 묶음의 명세 대부분을 연다. 가장 값이 크다. - 3은 중첩을 상태까지 넓힌다. - 4는 쌓인 것을 줄인다. 2 뒤에 해야 모을 곳(중첩, 갈래)이 생긴다. - 5는 모든 앱의 수를 건드리므로 마지막이다. ## 선행 - **EVM의 가스.** 끝없는 반복과 재귀를 허락하고, 끝남과 결정성을 가스로 얻는다. 가스가 다하면 그 호출의 상태 변경이 되돌아간다. 가스는 명령마다 명세의 표로 정한다. 모든 노드가 같은 가스를 센다. Pure의 예산, 원자성, "모듈이 단위"와 같은 꼴이다. - **파이썬의 정수 비트 연산.** 무한한 2의 보수 위의 뜻이다. Pure는 i64 안에서 같은 뜻을 쓰고, 넘으면 실패한다. - **Elm.** 모델, 업데이트, 뷰와 레코드, 커스텀 타입(갈래)이다. aup 앱의 모양과 같다. 영속, 트랜잭션, 전달은 없다. - **영속 자료 구조**(Clojure의 벡터와 맵). 구조를 나눠 스냅숏을 싸게 한다. Pure의 공유 리스트 사본이 그 한 예다(B+트리). ## 의심 (이 설계 안의) - **바이트와 text를 따로 둘 것인가?** 표현은 하나이고 타입만 둘이다. 불변식(UTF-8)이 보기와 경계에 필요해서 타입은 남긴다. - **앵커가 필요한가?** 항목에 키가 있으면 인덱스는 키다. text의 커서는 편집에 따라 옮겨야 하므로 남는다. 4단계에서 정한다. - **`number`(binary64)는 앱 런타임에 필요한가?** 물리와 그래픽에는 필요하다. 돈에는 맞지 않는다(5단계). - **세는 `for`가 필요한가?** 뜻으로는 `while`과 예산이면 된다. 다만 세기는 읽기 쉽고, 상한을 구현에 알려 준다. 남긴다. ## 1단계의 착수 기준 - **바꿀 결정.** 바이트, 비트, 수의 리스트로 SHA-256을 얇게 쓸 수 있으면 받아들이고, 2단계로 간다. - **남은 질문.** - 새 명령이 실행기, 묶음 실행기, JIT의 기존 길을 바꾸지 않는가? - 한 블록에 드는 명령 수와 시간은 얼마인가? - **최소 확인.** aup로 쓴 SHA-256, NIST 벡터, 파이썬 참조의 줄 수, C 대비 시간, 다섯 일과 은행 질의의 명령 수. - **중단 기준.** 기존 일의 명령 수가 막대를 넘으면 멈추고, 새 명령이 기존 길을 건드린 곳을 찾는다. SHA-256이 파이썬보다 길면, 무엇이 길게 했는지 적고 그것을 1단계에서 고칠지 정한다.