revo - 프로그래밍의 즐거움을 위한 동적 언어
revo - 프로그래밍의 즐거움을 위한 동적 언어
Zig로 작성된 동적 타입 언어 revo는 "프로그래밍의 즐거움"을 핵심 철학으로 삼고 있다. 현재 버전 0.0.1a로 초기 개발 단계이며, MIT 라이선스 하에 오픈소스로 공개되어 있다. GitHub(if-not-nil/revo)와 Codeberg(lung/revo)에서 호스팅되며, 410회 이상의 커밋이 쌓인 상태다.
한 줄 요약
revo는 Zig로 구현된 동적 타입 언어로, 파이프 연산자, 패턴 매칭, spawn 기반 동시성, 컴파일 타임 실행, 내장 테스트 프레임워크, LSP 서버 등을 갖추고 있다. "모든 것이 값을 반환한다"는 철학 아래 예외 대신 명시적 에러 값을 사용하고, Lua 같은 메타테이블 시스템을 통해 객체 지향을 구현한다.
핵심 특징
1. 파이프 연산자 (|>)로 데이터 흐름을 자연스럽게
revo는 값을 아래로 흐르게 하는 파이프 연산자를 핵심 문법으로 삼는다. _ 플레이스홀더로 직전 값을 참조할 수 있어 메서드 체이닝을 직관적으로 작성할 수 있다.
"hello!!"
|> _:upper()
|> _:sub(0, 4)
|> _ + ", world!"
|> print
이는 Elixir나 F#에서 볼 수 있는 파이프 패턴과 유사한데, revo는 이를 더 단순화해서 _만으로 직전 값을 참조할 수 있게 했다. 데이터를 여러 단계로 변환할 때 중첩 함수 호출 대신 위에서 아래로 읽히는 구조를 만든다.
2. 예외가 없는 에러 처리 — 에러는 값이다
revo에는 nil이나 불리언이 없고, 대신 원자(atom)가 사용된다. :nil, :true, :false가 원자이며, 에러는 (:ok, value) 또는 (:err, reason) 튜플로 반환된다.
const f = fs.open({path = "./readme.md"})
# (:ok, "파일내용") 또는 (:err, :IoError)
# ? 연산자로 언래핑 — 에러면 상위 함수로 전파
const f2 = fs.open({path = "./readme.md"})?
? 연산자는 :ok 값을 꺼내고, :err이면 현재 함수에서 즉시 에러를 반환한다. 최상위 레벨에서 에러가 전파되면 패닉이 발생한다. Rust의 Result<T, E>나 Elixir의 {:ok, value} / {:error, reason} 패턴을 따르지만, 더 간결한 문법으로 제공한다.
또한 orelse로 기본값을 설정할 수 있다:
const fallback = tonumber("nope") orelse 0
3. 모든 것이 표현식 — "everything is something"
revo의 핵심 철학은 "모든 것이 값을 반환한다"는 것이다. 변수 할당, 조건문, 함수 정의 모두 표현식이며 값을 반환한다.
let x = 10 # 10을 반환
let label = if x > 0 "positive" else "zero"
# 중첩 let도 마지막 값 반환
let a = let b = 5 # 5를 반환
이것은 Lisp 계열 언어에서 볼 수 있는 "문(statement)과 표현식(expression)의 구분 없음"을 채택한 것이다. 코드의 모든 블록이 값을 반환하므로 파이프나 패턴 매칭과 자연스럽게 연결된다.
4. 컴파일 타임 실행 (comp)
comp 키워드로 어떤 표현식이라도 컴파일 타임에 실행할 수 있다. 컴파일 타임 VM과 런타임 VM이 동일하므로, 런타임 코드와 컴파일 타임 코드가 완전히 호환된다.
# 빌드 시 사용자 입력을 받아 바이트코드에 고정
const x = comp read()
# 컴파일 타임 계산
const long = do
let t = 0
for i in 0..100
t += i
t
end
이는 Zig의 comptime이나 Rust의 const fn과 유사하지만, revo는 동적 언어임에도 동일한 VM을 사용하므로 런타임/컴파일 타임 경계가 훨씬 유연하다.
5. 프로시저럴 매크로
원시 AST 토큰을 변환하는 매크로 시스템을 제공한다. 반복자(iterator)를 통해 토큰을 읽고 새로운 AST를 반환한다.
proc cmul!(iter) do
let a = 10 + (iter:next_of(:number))
let b = iter:next_of(:number)
let c = iter:next_of(:number)
let acc = 0
for i in 1..5
acc += a * b + c
end
{(:number, acc)}
end
print(cmul!(10, 20, 30))
6. 패턴 매칭
원자와 튜플을 이용한 패턴 매칭을 지원하며, 가드(when)도 사용할 수 있다.
match (:ok, 42)
| (:ok, v) => v
| (:err, e) => panic(e)
| _ => panic()
# 가드와 함께
const response = match "hello!"
| "hello!" => "hi!"
| x when (x:len() > 10) => ""
| x when string?(x) => x + " to you too!"
| _ => ":("
7. 파이버(Fiber) 기반 동시성 — spawn 하나로 논블로킹
블로킹 코드를 논블로킹으로 바꾸려면 spawn 키워드만 붙이면 된다. 협력형(cooperative) 파이버를 사용하며 FIFO 큐로 실행된다.
while :true do
let conn = server:accept()?
spawn serve(conn, port - 1) # spawn만 붙이면 논블로킹
end
채널(channel)도 지원한다. chan(0)은 버퍼 없는 채널(수신자가 준비될 때까지 송신자 블로킹), chan(n)은 버퍼 있는 채널이다.
let ch = chan(5)
send(ch, "hello")
let msg = recv(ch)
이는 Go의 goroutine+channel 모델에서 영감을 받았지만, 협력형 스케줄링을 사용해 선점형(preemptive) 스레드의 복잡성을 피한다.
8. 테이블과 메타테이블 — Lua 스타일 객체 지향
모듈, 배열, 맵, 객체 모두 테이블로 표현된다. 메타테이블을 통해 다른 테이블에 메서드나 속성을 추가할 수 있다.
let mt = {
name = fn(self) self.name,
set_version = fn(self, v) self.version = v,
}
let rec = {name = "revo", version = 2}
set_metatable(rec, mt)
이는 Lua의 메타테이블 시스템을 그대로 따랐다. 클래스 없이도 객체 지향 프로그래밍이 가능하다.
9. 옵셔널 타입 시스템
타입 시스템은 선택적이지만 통합되어 있다. 타입이 지정된 코드는 더 빠르고 최적화되며 컴파일 타임 검증이 된다. 대부분의 타입은 추론된다.
type Result = (:ok, any) | (:err, atom)
struct User {
name: string = "me",
age: int = 21,
fn get_age(self) -> Result
(:ok, self.age)
end
}
let user = User{} # 타입 추론
print(user:get_age())
10. 일급 테스트 프레임워크
테스트가 언어에 일급으로 내장되어 있다. --test 플래그를 붙였을 때만 컴파일·실행된다. 테스트는 에러를 반환할 때 실패하는 클로저다.
suite "add" do
test "addition" do
expect(add(20, 22) == 42)?
expect(add(20, 22) != 22)?
end
test/skip "adds two tables" do
expect(add({1,2}, {3, 4}) == {4,6})?
end
end
11. 임베딩 API
revo.h 헤더 파일을 통해 C 코드에 직접 끼워 넣을 수 있다. erevo_vm_create, erevo_compile, erevo_run, erevo_eval 등의 API를 제공한다. Lua나 JavaScript 엔진을 임베딩하는 것과 같은 방식이다.
12. 내장 LSP 서버
revolt라는 LSP 서버가 내장되어 있다. 진단, 정의 이동, 호버 정보, 참조, 문서 심볼, 워크스페이스 심볼을 지원한다. 릴리스 빌드에 기본 번들되며 revo --lsp로 실행한다. Neovim 설정 가이드도 제공된다.
표준 라이브러리
| 모듈 | 설명 | 주요 함수 |
|---|---|---|
| fs | 파일/디렉토리 접근 | open(), read(), readdir(), stat() |
| json | JSON 인코딩/디코딩 | encode(), decode() |
| math | 수학 연산 | abs(), sqrt(), sin(), cos(), pi |
| time | 시간 처리 | now(), now_ns(), sleep() |
| net | TCP 소켓 | listen(), accept(), connect(), send(), recv() |
| os | 시스템 접근 | stdin/stdout |
| system | 서브프로세스 | system({"cmd", "arg"}) |
| fmt | 문자열 포맷팅 | 포맷 함수들 |
설치 및 빌드
바이너리 릴리스는 아직 없으며 Zig 컴파일러(0.16.0)로 소스에서 빌드해야 한다.
git clone https://github.com/if-not-nil/revo
cd revo
zig build -Doptimize=ReleaseFast
./zig-out/bin/revo -e 'print("hello world")'
Windows는 비동기 백엔드와 완전한 라인 에디터가 아직 미구현 상태다.
새로운 시각
revo는 여러 언어의 장점을 조합한 "잡종 언어"다. Lua의 경량성과 메타테이블, Elixir의 파이프와 에러 처리, Go의 동시성 모델, Zig의 컴파일 타임 실행, Lisp의 "모든 것이 표현식" 철학을 하나의 언어에 녹여냈다.
흥미로운 점은 Zig로 구현된 동적 언어라는 것이다. 보통 동적 언어는 C로 구현되는 경우가 많지만, Zig의 메모리 안전성과 빌드 시스템을 활용하면서도 동적 타입의 유연성을 제공한다. 또한 컴파일 타임 VM과 런타임 VM이 동일하다는 점은 Rust/Macro 시스템과 달리 동적 언어에서도 컴파일 타임 메타프로그래밍이 자연스럽게 작동함을 의미한다.
"모든 것이 값을 반환한다"는 철학은 코드의 각 블록이 조합 가능함을 보장한다. 이는 함수형 프로그래밍의 핵심 아이디어를 절차형 문법 위에 얹은 것으로, 러닝 커브를 낮추면서도 함수형 패턴을 사용할 수 있게 한다.
미래 영향
revo는 아직 0.0.1a 단계로 초기 개발 중이지만, 몇 가지 잠재적 영향을 생각해 볼 수 있다:
- 스크립팅 언어로서의 위치: Lua, Python, JavaScript 사이에서 "가벼우면서 강력한" 틈새를 노릴 수 있다. 특히 임베딩 API가 완비되면 게임 엔진, IoT 디바이스, 설정 언어 등으로 활용 가능성이 있다.
- 동시성 모델:
spawn하나로 논블로킹이 되는 모델은 Go의 영향이 크지만, 협력형 파이버라 선점형 스케줄링의 복잡성이 없다. 소형 서버나 네트워크 프록시에서 유용할 수 있다. - 교육용 언어: 파이프 연산자와 패턴 매칭, 에러-as-value 등을 간결한 문법으로 제공하므로 프로그래밍 교육 도구로도 잠재력이 있다.
하지만 410 커밋, 릴리스 2회라는 점으로 보아 아직 생태계나 커뮤니티가 작다는 점이 현실적인 한계다. Zig 0.16.0에 종속되어 있다는 것도 고려해야 한다.
관련 노트
- 2026-06-12_why-ai-hasnt-replaced-software-engineers — AI 시대에 프로그래밍 언어의 역할에 대한 논의
- [[2026-06-08_zig-async-new-plan]] — Zig의 비동기 계획과 관련