SkyHetu: Rust로 인과성‑우선 프로그래밍 언어 설계
Source: Dev.to

제가 경력 동안 마주한 대부분의 버그는 한 가지 질문으로 귀결됩니다: “이 변수가 어떻게 여기까지 왔나요?”
현대 프로그래밍 언어는 기억 상실증 환자와 같습니다. user.balance가 0이라면, 애플리케이션은 현재 그것이 0이라는 사실만 알고, 그 상태에 이르게 된 일련의 사건들을 기억하지 못합니다. 논리 오류였을까요? 레이스 컨디션? 아니면 불량 함수 호출? 이를 알아내기 위해 우리는 print 문을 삽입하고, 디버거를 연결하며, 머릿속으로 역사를 재현하려 애씁니다.
저는 뭔가 다른 것을 만들기로 결심했습니다. SkyHetu라는, **인과성(Causality)**이 일급 시민인 프로그래밍 언어를 만들었습니다.
The Philosophical Shift
SkyHetu에서는 단순히 상태를 바꾸는 것이 아니라 이벤트를 생성합니다. 이를 위해 저는 세 가지 급진적인 설계 결정을 강제했습니다:
- Immutability by Default: 변수를 명시적으로 요청하지 않으면 변경할 수 없습니다.
- Explicit State: 가변 변수는
let이 아니라state로 선언합니다. - The Arrow Operator (
->): 할당(=)은 초기화에만 사용하고, 화살표(->)는 변이에 사용합니다.
state counter = 0
counter -> counter + 1
-> 연산자는 두 가지 일을 수행합니다:
- 값을 업데이트합니다.
- 인과성을 로그에 기록합니다.
Building the Time Machine in Rust
이를 구현하려면 메모리와 실행 흐름에 대한 고성능 저수준 제어가 필요했기에 Rust를 선택했습니다. SkyHetu의 핵심은 스택 기반 가상 머신(VM)이며, 스택과 힙 옆에 CausalityLog가 존재합니다.
pub struct CausalityLog {
history: HashMap>,
clock: usize, // Logical time
}
pub struct MutationEvent {
pub old_value: Value,
pub new_value: Value,
pub timestamp: usize,
pub location: Option,
}
VM이 OP_TRANSITION 명령어( ->에 의해 트리거됨)를 실행할 때마다 이전 값과 이후 값을 스냅샷으로 캡처하고, 논리 시계 틱을 찍어 히스토리에 푸시합니다.
The “Detective Board” Visualization
언어가 모든 것을 기억하기 때문에 디버깅이 고고학보다 탐정 작업에 가깝게 느껴집니다. 저는 causal_graph()라는 네이티브 함수를 구현했으며, 이는 내부 히스토리를 DOT 포맷(Graphviz)이나 JSON으로 내보냅니다.
state score = 100
score -> score - 10 // event 1
score -> score - 50 // event 2
print(causal_graph("score", "dot"))
생성된 그래프는 각 상태를 노드로, 타임스탬프가 찍힌 전이를 엣지로 표시합니다—코드를 위한 “탐정 보드”입니다.
Why Rust?
Rust가 완벽한 선택이었던 이유는 세 가지입니다:
- Enums: 동적 타입(
Value::Number,Value::String)을 표현하는 것이 간단하고 안전합니다. - Performance: 인과성을 추적하는 오버헤드가 존재하지만, Rust의 제로 코스트 추상화 덕분에 핵심 경로를 최적화할 수 있습니다.
- Correctness: 가비지 컬렉터와 VM을 구현하는 일은 복잡합니다. 빌림 검사기(borrow checker)가 수많은 세그멘테이션 오류로부터 저를 구해주었습니다.
The Result
SkyHetu는 단순한 장난감이 아니라 모듈을 컴파일하고, 클로저를 다루며, 클래스까지 지원합니다. 다른 언어와 달리 내장된 introspection을 제공하므로, 언어에 “왜 X가 참인가?” 라고 물으면 인과성 체인을 얻을 수 있습니다.
print(why(counter))
// Causality chain for 'counter':
// 1. [t=1] 0 -> 1
// 2. [t=2] 1 -> 2
우리는 디버깅에 전체 시간의 90 %를 소비합니다. 어쩌면 우리 언어가 나머지 10 %를 도와줄 때가 된 것 아닐까요?