요약
Jank는 헤더만 불러오면 C++ 코드를 바로 쓸 수 있는 Clojure 방언입니다.
Clojure는 보통 자바와 연동해 쓰는 리스프 계열 언어입니다. Jank는 그 자리에 C++를 놓고, C++로 컴파일한 뒤 LLVM(컴파일러 기반 기술)으로 네이티브 실행 파일을 만듭니다.
왜 중요한가
- 브리지(두 언어를 잇는 별도 등록 코드) 없이 기존 C++ 라이브러리를 그대로 가져다 쓸 수 있습니다.
- 제작자는 Jank로 C++ 앱을 만들거나, 기존 C++ 앱에 Jank를 끼워 넣을 수 있다고 설명했습니다.
- 게임처럼 C++ 라이브러리에 기대는 분야에서도 REPL(코드를 바로 실행해 보는 대화형 환경) 개발이 가능해집니다.
핵심 내용
- 템플릿, 가상 함수, 예외, 오버로드 해석, RAII 같은 C++ 기능이 추가 코드 없이 동작합니다.
- Clang 기반 JIT(실행 중에 코드를 컴파일하는 방식)로 nREPL 호환 편집기에서 대화형 개발을 지원합니다.
- 미리 컴파일(AOT)하면 작은 실행 파일이 나오고, 시작도 거의 즉시 된다고 합니다.
- Boost로 파일 크기를 읽고 C++ 예외를 잡는 예제와, GLFW·Dear ImGui를 직접 부르는 게임 예제를 보여 줍니다.
- 제작자는 HN 댓글에서 지금은 알파 단계이고, 1월에 더 안정적인 베타를 낼 계획이라고 밝혔습니다.
HN 반응
- 네이티브 코드를 REPL에서 바로 고쳐 보는 방식이 AI 에이전트 개발에 잘 맞는다는 기대가 여럿 나왔습니다.
- 반면 에이전트 작업에서 컴파일 시간은 사소하고, 실행 중 쌓인 상태로 얻은 결과는 믿기 어렵다는 반론도 있었습니다.
안녕하세요, jank를 만든 사람입니다.
jank는 전례 없는 수준의 C++ 상호운용성을 갖춘 완전한 Clojure 방언입니다. 원조 Lisp/C++ 조합인 Clasp와 달리, C++ 함수나 타입, 값을 등록하려고 브리지 API를 쓸 필요가 없습니다. 헤더만 include하면 어떤 C++ 코드와도 매끄럽게 상호운용됩니다. 템플릿 인스턴스화, 오버로드 해석(암시적 변환, ADL 등), 예외 던지기와 잡기, 가상 함수 호출을 비롯해 말 그대로 모든 것이 됩니다.
jank는 _C++ 그 자체_입니다. C++로 컴파일되고 C++ 타입 시스템을 쓰니까요. REPL의 편의 기능이 전부 동작하도록 Clang 기반 JIT C++ 컴파일러도 함께 제공합니다. 또 Cargo 같은 도구를 갖춰서 C++ 빌드 생태계도 개선합니다.
그래서 jank로 C++ 네이티브 애플리케이션을 만들 수도 있고, 여러분의 C++ 애플리케이션에 jank를 내장할 수도 있습니다. 다른 Clojure 방언 사용자들이 가져다 쓰는 이식성 있는 Clojure 라이브러리를 jank로 작성하는 것도 가능합니다.
아직은 알파 단계라 불안정한 부분이 있습니다. 1월에 좀 더 안정적인 베타 릴리스를 내는 것이 목표입니다. 가장 큰 도움이 되는 방법은 jank를 직접 써 보고, 불편한 점을 하나도 빠짐없이 Slack, Discord, GitHub에 알려 주시는 것입니다.
저는 몇 년째 jank를 만들고 있습니다. 바이브 코딩으로 만든 게 아닙니다.
네이티브 코드에 접근할 수 있는 REPL 주도 개발은 에이전트 엔지니어링의 마법 공식이 될 수 있다고 봅니다. 재시작도, 재컴파일도 없으니까요. 게다가 영속적이고 불변인 자료구조 덕분에 정신을 놓지 않고도 여러 스레드로 병렬 반복 작업을 할 수 있습니다.
맞습니다. 에이전트가 제 Clojure 코드베이스를 얼마나 빨리 파악해서 이것저것 시도해 보는지 놀랍습니다. REPL을 아주 잘 활용합니다.
저는 취미 프로젝트에서 Clojure를 조금씩 만지고 있습니다. 요즘은 에이전트가 REPL을 쓰게 하려면 어떻게 설정하는 게 좋은가요? 예전에는 에이전트가 REPL을 쓰게 만드는 데 애를 먹었습니다.
https://github.com/babashka/nrepl-client
이제 괄호를 믿고 맡길 만큼 제대로 닫나요? 예전에 써 보고 실망했는데, 다시 한번 해 볼 만한 수준이 됐다는 말씀 같아서요.
Opus 5.5에서 이 부분이 많이 좋아졌습니다. 예전에는 코드베이스를 망가뜨려서 정말 짜증이 났거든요.
Clojure / ClojureScript 코드베이스 기준입니다.
초기 모델에서 있던 문제입니다. 1년도 더 전의 일이라고 봐야 하고, 그때도 편집에 clojure-mcp 도구를 쓰면 이 문제는 완전히 해결됐습니다.
지금은 괄호 균형을 맞추려고 clojure-mcp를 쓸 필요조차 없습니다. 모델이 알아서 아주 잘 해냅니다.
감사합니다! 네, 제가 써 본 게 아마 1년도 더 전이었을 겁니다. 통계적으로 움직이는 프로그램이 Lisp 코드를 짜면서 닫는 괄호 개수를 감으로 정한다고 생각하니 소름이 돋았거든요.
이런 질문이 나온다는 게 정말 놀랍네요.
gpt-3.5 때는 맞는 말이었지만 세상은 이미 한참 달라졌다고요, 친구!
"요즘 영화에는 소리가 나오나요?"라고 묻는 거나 마찬가지예요. 답은 "네, 소리뿐 아니라 컬러까지 나옵니다!"고요.
똑같다고 보기는 어렵습니다. 소리 나는 영화를 본 적이 없으려면 수십 년 동안 세상과 담쌓고 살아야 합니다. 괄호 문제가 해결됐는지 모르는 데는 1년쯤 안 써 보고 Lisp 커뮤니티에 속해 있지 않기만 하면 충분합니다.
저는 LLM의 능력에 관해서는 제가 모른다는 걸 가장 먼저 인정하는 사람입니다. 그래서 솔직하게 물어본 겁니다.
아니요, 문제는 여전히 있습니다. 나아졌을 뿐이에요.
이분이 비추천을 받을 일은 아니죠. 다만 저는 괄호가 깨지는 걸 본 지 꽤 됐습니다.
재시작도 재컴파일도 없다고 하니 제일 먼저 떠오르는 건, 프로토타입을 만든다고 이것저것 만지다가 어떤 셀을 실행했는지 잊어버리면 Jupyter에서 변수가 엉망이 되는 상황입니다. 에이전트도 분명 그런 문제를 겪을 것 같아요. 저라면 가능한 한 컴파일을 빠르게 만드는 쪽으로 가겠습니다.
불변성과 무상태가 nREPL과 Jupyter를 갈라놓는 차이입니다. Clojure가 엄격한 함수형 언어는 아니지만, 대부분의 작업을 가장 수월하게 하는 방법이 대개 함수형 방식입니다.
무엇이 "함수형 언어"를 규정하는지는 역사를 슬쩍 비껴가는 경우가 많습니다. Clojure는 기본이 불변 자료구조라서 조상격인 Lisp보다 더 함수형이고, Lisp도 아주 교조적인 프로그래밍 모임이 아니면 "함수형"이라는 지위를 누립니다.
함수형 프로그래밍의 요건은 원거리 작용(action from a distance)이 없다는 것입니다. 그러니까 함수가 자기 스코프 밖의 변수를 수정하면 안 됩니다.
불변 자료구조가 반드시 있어야 한다는 뜻은 아니지만, 도움은 됩니다.
어쨌든 Lisp(또는 Common Lisp)에서 자료구조를 다루는 함수 대부분은 불변 방식으로 동작합니다.
그건 특정 커뮤니티의 기준을 투영한 요건일 뿐이고, 이 관행이 역사적으로 진화해 온 과정을 무시하는 겁니다. 이제 기계가 이런 용어의 미묘한 차이에 관해 일부 인간 엔지니어보다 더 유연하고 더 잘 이해한다는 점이 무척 흥미롭네요. 어차피 이렇게 현학적인 얘기를 겹겹이 쌓고 있으니 말인데, '요건'보다는 '윤리'나 '관행'이 여기서는 더 효과적이고 실용적이며 포용적인 표현입니다. 이 스레드에 깔린 전제, 즉 Clojure는 "충분히 함수형인" 언어가 아니라서 "함수형" 언어라고 부를 수 없다는 생각은 좁은 커뮤니티의 편견입니다.
이미 LLM으로 Clojure REPL을 돌리는 사람들은 잘 된다고 합니다(Clojure Slack의 #ai-driven-development를 보세요). 어떤 셀을 실행했는지 잊어버리는 문제는 지금 설명하는 구성에서는 문제가 아니라고 생각합니다.
정확히는 #ai-assisted-coding입니다.
네, 감사합니다!
에이전트 프로그래밍에서 이게 정말 문제인가요? 저는 꽤 복잡한 작업도 에이전트로 해 봤는데, "컴파일 속도만 느리지 않았어도 훨씬 빨랐을 텐데"라고 생각한 적이 한 번도 없습니다. Claude가 걸리는 시간에 비하면 빌드 시간은 아무것도 아닌 것 같거든요.
물론 저는 100만 줄이 넘는 코드베이스를 다루지는 않아서, 그런 경우라면 느린 컴파일이 문제가 될 수 있겠다는 건 이해합니다. 그래도 그때는 에이전트 쪽도 같이 느려질 것 같고요.
상태 관리는 원래 어렵고 복잡한 문제로 통합니다. 에이전트(든 사람 프로그래머든)가 상태를 초기화하지 않고, 아무것도 재시작하지 않고, 도무지 현실성 없는 의식(ritual) 같은 절차도 없이 작업을 반복할 수 있게 해 주면, 추측하는 걸 멈추고 현재 상태를 경험적으로 확인하면서 훨씬 촘촘한 루프로 문제 공간을 추론하게 됩니다. 그러면 토큰도 시간도 노력도 덜 드는 해법이 나옵니다.
Flappy Bird 같은 게임을 만든다고 해 보세요. 움직이는 물체의 궤적 계산을 조금 손볼 때마다 다시 컴파일해야 합니다. 프로그래머가 값을 실시간으로 만져 보는 쪽과, 다시 컴파일하고 재시작하고 그 시점까지 상태를 되돌려 놓고 어디까지 왔는지 확인하는 쪽 중 어느 쪽이 더 간단할까요?
Lisp REPL로 프로그래밍하다 보면 비디오 게임을 하는 기분이 들 때가 많습니다. 재미있을 뿐 아니라 (비교하면) 훨씬 더 합리적이기도 합니다.
저는 Lisp을 좋아하고, REPL을 통째로 깎아내리려던 건 아닙니다. 그런데
프로그래머와 에이전트는 근본적으로 다르고, 필요한 접근법도 다르다고 생각합니다. 프롬프트 몇 개에 30분 넘게 걸리기도 하는데, 에이전트가 재컴파일을 피해서 얻는 이점이 뭔지 모르겠습니다. 제 에이전트가 하는 일 가운데 컴파일 시간이 1%라도 될지 의문입니다. 틀렸을 수도 있지만요(다시 말하지만 저는 거대한 걸 다루지는 않습니다).
또 현재 이미지가 의도대로 동작한다는 이유로 에이전트가 문제를 해결했다고 가정하는 것도 원치 않습니다. 에이전트가 제대로 된 해법을 가졌는지, 아니면 아주 특정한 상태를 쌓아 올려서 그 상태에서만 동작하고 처음부터 시작하면 같은 결과가 안 나오는지를 알아볼 거라고 믿지 못합니다.
그리고 국소적인 테스트는 에이전트에게 맡기는 것보다 제가 REPL로 직접 하는 쪽이 마음 편합니다. 문제가 해결됐다고 가정하게 두기보다는, 더 넓은 범위의 동작을 확인할 테스트 스위트나 하네스 같은 게 필요할 겁니다. 다만 이게 오래 문제로 남을 것 같지는 않습니다.
시간 문제가 아닙니다. 요점을 놓치고 계신 것 같아요. 핵심은 "몰입을 유지하는 것"이고, 이 점에서는 사람이나 에이전트나 크게 다르지 않습니다.
게다가 제 댓글의 핵심 논점인 상태는 그냥 무시하고 계시네요. 앱의 특정 동작을 조작하고 바꾸다가, 특정 순간 특정 값 조합이 만든 상태를 정확히 복원해야 했던 적 없으세요? 다시 컴파일하고 다시 로드해야 하면 이런 상황을 처리하려고 어쩔 수 없이 정교한 스크립트 발판을 만들게 됩니다. 라이브 이미지 REPL은 이 문제를 아예 종류째로 없애 줍니다. 기본이 불변이라는 점은 그걸 더 단순하게 만들고요.
에이전트는 그냥 실시간으로 평가해 보면서 증명하면 됩니다. 물론 처음부터 시작해도 똑같이 동작한다는 보장은 없지만, 어느 쪽이든 동작을 테스트해야 한다는 걸 부정하는 게 아닙니다.
절대적으로 다릅니다. LLM은 멈췄다가 정확히 같은 자리에서 다시 이어 갈 수 있습니다. 컴파일이 1초가 걸리든 한 시간이 걸리든 아무 차이가 없어요. 사람은 자기가 무슨 일을 하고 있었는지 기억해 내야 하고요. 사람들이 몰입 상태라고 할 때 말하는 게 바로 그겁니다.
무시하지 않았다고 생각합니다. 직접 얘기했거든요. 컨텍스트가 열화되면 LLM이 실시간 평가에 지나치게 기댈 수 있어서 오히려 문제가 더 생길 수 있다고 했습니다. 저는 불변 언어에서 쓰는 쪽이 더 마음 편할 것 같습니다.
에이전트한테 뭘 더 좋아하는지 물어보세요.
제가 Clojure에 푹 빠져 있던 몇 년 전에 이게 있었으면 좋았을 텐데요. 정말 멋집니다!
clasp(Common Lisp C++ 구현체)와 비교하면 어떤지 궁금하네요?
접근 방식이 아주 다릅니다. 이에 관해서는 여기에 댓글을 달았습니다. https://news.ycombinator.com/item?id=50212578