일반인을 위한 Janet (2023)
(ianthehenry.com)- Janet for Mortals는 Janet 프로그래밍 언어를 다루는 무료 온라인 책이지만, 회고의 초점은 책 자체보다 웹 REPL과 예제 프로젝트까지 포함한 기술서 제작 과정에 있음
- 전체 작업은 20주가 걸렸고, 순수 집필 예상치였던 12주를 넘어 웹사이트, C++ 바인딩, 브라우저 데모, CLI 파서, 테스트 도구 같은 부가 작업이 붙었음
- 책 웹사이트는 CodeMirror와 Lezer 문법을 활용한 내장 REPL을 제공하며, 자동완성은 WebAssembly로 Janet 환경을 조회하고
(report)는 SQLite에 피드백을 저장함 cmd,judge,to do, Toodle.Studio는 Janet이 스크립팅, CLI 도구, 테스트, 브라우저 실행, JavaScript 상호운용에 쓰일 수 있음을 보여주는 기반 사례로 쓰임- 출시 직후 Hacker News와 Lobsters 제출만으로 금요일 30,025명, 토요일 9,568명, 일요일 3,777명의 고유 방문자가 있었지만, 5개 이상 챕터를 읽은 고유 IP는 387개로 추정됨
책의 범위와 실제 작업량
- Janet for Mortals는 Janet 프로그래밍 언어를 다루는 무료 온라인 책임
- 전체 제작 기간은 여가 시간을 기준으로 20주였음
- 초기 예상은 12주였고, 영어 본문 집필 시간만 놓고 보면 크게 빗나가지 않았음
- 실제 일정에는 책 주변의 코딩 프로젝트가 크게 포함됨
- 최종 분량은 코드 조각을 제외하고 영어 산문 44,000단어임
- 비교 대상으로 든 The Great Gatsby는 47,000단어
- 이전 Nix 관련 글 시리즈의 절반 이하 길이임
- 5개월 동안 책과 함께 다음 작업도 진행됨
웹사이트와 내장 REPL
- Janet for Mortals 웹사이트의 핵심 장치는 내장 REPL임
- 사용자는 언제든 Escape 키로 REPL을 열 수 있음
- REPL은 페이지 하단에 고정되어 본문을 크게 가리지 않음
- 편집기는 CodeMirror를 사용함
- CodeMirror는 기본적으로 Janet을 지원하지 않음
- 이전 Bauble 작업 때 만든 Janet 언어 지원을 기반으로 삼음
- Bauble DSL에서는 중요하지 않았던
multi`backtick`quoted문자열 같은 Janet 문법도 책에서는 필요했음
- CodeMirror가 사용하는 Lezer 문법을 보강하면서, 같은 문법을 책 코드 조각의 문법 강조에도 재사용함
- 알려진 정적 사이트 생성기 중 Janet 코드 강조를 지원하는 것을 찾지 못함
- 처음에는 책이 흑백이었지만, 낯선 Janet 문법을 더 친근하게 보이도록 색상을 추가함
- 정적 사이트 생성기는
redo를 사용해 직접 작성함- 생성기의 핵심은 JavaScript로 작성되어 Lezer 문법을 붙일 수 있었음
- 전체 구성은 shell로 연결된 취약한 구조였음
- Remark는 책 원문 파싱과 클라이언트 REPL 양쪽에서 활용됨
- 코드 블록에 라벨을 붙이는 간단한 확장을 추가함
- Janet 표준 라이브러리 docstring은 Markdown으로 작성되어 있어, REPL 자동완성에서 즉석으로 HTML 렌더링됨
자동완성과 단방향 피드백
- REPL 자동완성은 Janet을 처음 접하는 독자를 위해 중요하게 다뤄짐
- REPL 시작 시 WebAssembly를 통해 Janet 환경을 동적으로 조회함
- 사용자가 docstring이 있는 새 심볼을 정의해도 자동완성 결과에는 나타나지 않음
- 명령 실행 후마다 자동완성을 다시 생성할 수도 있지만, 그만한 가치가 크지 않다고 판단함
(report)함수는 문자열을 받아 간단한 웹 서버로 POST하고, 서버는 이를 SQLite 데이터베이스에 저장함- 댓글 상자와 비슷하지만 REPL에서 바로 보내는 방식이 특징임
- 이 기능으로 피드백을 읽고 여러 오류를 수정함
- 답장할 방법은 구현하지 않았음
- report 백엔드는 Janet이 아니라 작은 Haskell 애플리케이션임
- POST 요청을 받아 SQLite 데이터베이스에 넣는 역할만 함
- 웹 서버는 Janet으로 만들지 않음
- 웹 서버에서 가장 중요한 요소를 보안으로 봄
- Janet과 Janet HTTP 라이브러리가 인터넷에 직접 연결할 만큼 충분히 검증됐다고 보지 않음
jimmy: C++ 상호운용 실험
jimmy는 immer에 대한 Janet 바인딩임- immer는 영속 자료구조 라이브러리임
- 바인딩은 완성되지 않았고, 사용할 일이 생기기 전까지는 완성하지 않을 가능성이 큼
- 그래도 Janet에서 C++ 코드와 상호운용하는 방법을 보여주는 예시로는 성공적이었다고 봄
Toodle.Studio와 브라우저 안의 Janet
- Bauble은 이전에 만든 작은 아트 놀이터이며, 브라우저에 Janet을 임베드한 첫 경험이었음
- 당시 Janet 임베딩 자료가 많지 않았음
- 브라우저에서 WebAssembly, Emscripten, TypeScript를 함께 써야 해 난도가 높았음
- Bauble의 최종 결과는 애플리케이션 로직이 Janet으로 작성된 웹사이트였음
- UI에는 JavaScript가 많이 쓰였지만 핵심 로직은 Janet이 담당함
- 브라우저에서 Janet을 쓰는 기술을 더 접근 가능하게 만들고 싶은 동기가 책 집필의 중요한 이유 중 하나였음
- 책에서는 Bauble 대신 Toodle.Studio를 예제로 만들었음
- Bauble은 상태가 없고 문자열을 문자열로 바꾸는 순수 함수에 가까워 상호운용 관점에서는 단순했음
- Toodle.Studio는 Bauble의 명백한 포크지만 JavaScript와 Janet 사이의 상호운용이 더 복잡함
- Toodle.Studio는 책의 예제로 더 적합한 기술 요소를 갖고 있었음
- 장시간 실행되는 Janet 프로그램을 시간에 걸쳐 비동기로 실행해야 함
- JavaScript 코드가 같은 Janet 값을 여러 참조로 보유하므로 메모리 관리를 고려해야 함
- 복잡한 중첩 자료구조를 C++를 거쳐 Janet과 주고받아야 함
- Toodle.Studio 로고는 Bauble로 만든 애니메이션 거북이임
- turtle graphics 데모인데 거북이가 없다는 피드백을 받고 추가함
- 눈이 마우스를 따라가도록 만들었음
cmd: Janet용 CLI 인자 파서
cmd는 명령줄 인자 파싱 라이브러리임- 책에서는 세 문단 정도만 등장하지만, Janet을 좋은 스크립팅 언어라고 명확히 보여주기 위해 필요했음
cmd는 OCaml의Core.Command에서 강하게 영향을 받음Core.Command는 사용해 본 명령줄 인자 파싱 라이브러리 중 최고였다고 봄- Janet에서 그 경험을 재현하려 했음
cmd는Core.Command만큼 좋지는 않음- 타입이 있으면 모든 것이 훨씬 쉬워진다고 봄
- 그래도 중요하게 여기는 기능의 95% 를 갖췄고, 간결한 표기 덕분에 ad-hoc 스크립트에서 쓰기 더 편하다고 봄
- 아직 Bash 자동완성 함수 생성 기능은 빠져 있음
Core.Command는 이를 자동 생성함cmdAPI는 나중에 이 기능을 추가할 수 있도록 설계됨
judge: 인라인 스냅샷 테스트 프레임워크
judge는 2021년에 Janet으로 처음 만든 것 중 하나임- Janet을 더 잘 알게 된 뒤 처음부터 다시 작성함
- API가 더 쓰기 좋아짐
- 구현도 훨씬 단순하고 변경하기 쉬워짐
- Judge v1과 v2의 주요 차이는 테스트 위치와 헬퍼임
- 테스트가
test/디렉터리뿐 아니라 일반 소스 파일 안에도 들어갈 수 있음 test-macro와test-stdout헬퍼가 추가됨
- 테스트가
test-stdout의 OCaml 대응 방식은 직업적으로 테스트를 작성할 때 거의 유일하게 쓰는 방식이라고 함- OCaml은 임의 데이터를 소스 코드에 임베드하는 방법이 마땅치 않아 모든 것을 문자열로 바꾸는 식으로 접근함
- 책 출간 후 Judge에
--interactive모드를 추가함- 책에는 Judge로 테스트와 디버깅을 다루는 챕터가 있음
to do: Bash 대신 Janet으로 만든 할 일 도구
to do는 스크립팅 챕터 예제로 선택됨- 이전에 Bash로 만들었던 비사소한 도구였고, Bash 구현은 고통스러웠음
- Bash에서 특히 어려웠던 부분은 텍스트 파싱과 날짜 처리였음
- Sed로 여러 줄 텍스트를 파싱하는 일은 즐겁지 않았음
- macOS와 Linux에서 같은 방식으로 동작하는
date조작은 사실상 불가능하다고 봄
- Janet에서는 PEG,
sh,cmd를 활용해 원래 Bash 할 일 목록보다 빠르게 더 많은 기능을 만들 수 있었음- 예로
fzf다중 선택 기능을 추가함 - Bash에서 null-terminated 문자열을 구성하는 일은 어렵다고 봄
- 예로
- 책에는 단순화된 버전만 다룸
- 미래 작업 예약 기능 없음
- 작업을 “건너뛰는” 개념 없음
- 핵심 기능은 목록에 항목 추가와 완료 처리임
zsh-autoquoter와 함께 쓰면 의외로 유용한 앱이라고 봄
집필 중 코드 작업과 유지보수
- 책 작업 20주 중 코드 작성은 거의 절반에 가까웠음
- 8주는 코드 작업이었고, 일부 기간에는 집필과 코딩을 동시에 진행함
- 처음에는 Judge 개선과 인자 파서 작성만 예상했음
- 인자 파서 구현은 예상보다 훨씬 어려웠음
- Bauble이 너무 단순해서 새 아트 놀이터를 또 만들게 될 줄은 예상하지 못했음
- 실제 집필은 Markdown을 Sublime Text에서 작성함
- Sublime Text는 긴 산문을 쓰는 데 선호하는 에디터임
- 집필 중 Janet 새 버전이 2개 나왔음
- 디버깅과 네이티브 모듈 챕터를 언어 변경에 맞춰 업데이트해야 했음
- 최신 Janet 릴리스에 맞춰 책을 유지할 계획임
출시 반응과 읽힌 정도
- 책 홍보는 Hacker News 제출, Lobsters 제출, 트윗 정도였음
- Hacker News에서는 하루 종일 프런트 페이지에 있었고 한동안 1위를 차지함
- Nginx 접근 로그 기준 고유 방문자는 다음과 같음
- 금요일: 30,025명
- 토요일: 9,568명
- 일요일: 3,777명
- 이 수치는 링크를 클릭한 사람 수에 가까우며, 실제로 책을 읽은 사람 수는 훨씬 적다고 봄
- 사이트에는 클라이언트 측 행동 추적 분석이 없음
- 접근 로그만으로 대략 추정함
- 챕터 방문 흐름은 유지율이 좋지 않아 보였음
- 1장은 홈 페이지 방문자의 22%
- 2장은 1장 방문자의 20%
- 3장은 2장 방문자의 69%
- 4장 “Pegular Expressions”와 9장 “Xenofunctions”는 3장보다 방문자가 많았음
- 출시 주말 종료 시점 기준, 실제 독자 수에 대한 최선의 추정치는 387명임
- 5개 이상 서로 다른 챕터를 로드한 고유 IP 수를 기준으로 삼음
이름 유래와 피드백에서 드러난 한계
- Janet 언어 이름은 The Good Place의 불멸 존재 Janet에서 왔음
- 이 존재는 사후 세계를 이동하는 mortals를 돕기 때문에 책 제목과도 연결됨
- 방문이 가장 적은 챕터는 “Testing and Debugging”이었음
- 책의 끝에서 세 번째 챕터임
- 마지막 세 챕터가 책에서 가장 흥미롭다고 봄
- 그 챕터의 테스트 방식은 개인 엔지니어링 경력에서 가장 큰 생산성 향상 중 하나였다고 봄
- 내장 REPL의 report 기능으로 494개 피드백을 받음
- 다수는 “좋은 책” 또는 테스트성 메시지였음
- 수십 개는 오타 제보, 설명 요청, 유용한 코멘트였음
- 일부 사용자는 연락처 없이 질문을 보냈고, 답장 기능이 없어 응답할 수 없었음
- report 기능은 매우 원시적인 단방향 피드백 기능으로 구현됨
댓글과 토론
Hacker News 의견들
-
같은 저자의 https://bauble.studio/도 빼놓을 수 없음
내 낡은 컴퓨터의 브라우저에서는 안 돌아가지만, 도서관에서 공부하다 쉬고 싶을 때는 도서관 컴퓨터로 bauble을 30분쯤 만지며 놀곤 함
숫자만 이리저리 바꿔도 만들 수 있는 그래픽이 엄청나고, Janet이 오래가길 바람- 2021년형 MacBook Pro M1 Max, MacOs 15.1.1에서는 Chrome에서는 동작하지만 Firefox에서는
unknown symbol twist컴파일 오류가 남
error: script:16:1: compile error: unknown symbol twist
in evaluate [lib/evaluator.janet] on line 81, column 7
in bauble-evaluator/evaluate [lib/init.janet] on line 8, column 12
- 2021년형 MacBook Pro M1 Max, MacOs 15.1.1에서는 Chrome에서는 동작하지만 Firefox에서는
-
Janet을 GNU Guile과 비교해보는 게 흥미로움
Guile의 원래 장점은 C와 나란히 쓰기 쉽게 만든 언어라는 점인데, 오래된 언어라서 언어 도구체인에 패키지 관리자를 기본 포함하는 식의 최신 감각은 아직 받아들이지 못한 듯함
Guile에서 마음에 드는 점은 RNRS 표준과의 호환성을 설명하는 데 많은 노력을 들였다는 것임
Janet이 실제로 얼마나 Scheme다운지는 모르겠지만 궁금함- Janet은 전혀 Scheme답지 않음
누군가 Lua 위에 Clojure를 만든 것에 더 가까움
- Janet은 전혀 Scheme답지 않음
-
책은 https://janet.guide/이고, 언어는 https://janet-lang.org/임
-
이 책의 실용적인 접근이 좋았고, 지금 Everybody Codes를 Janet으로 풀면서 배우는 큰 이유가 됨
https://everybody.codes -
“거북이는 은유”라는 표현은 아쉬움
거북이는 은유가 아니라 실제였음: http://classicacorn.computinghistory.org.uk/8bit_focus/logo/...
물론 “다른 것을 나타내는 어떤 것, 상징”이라는 은유의 정의에는 맞지만, 플로피 디스크 저장 아이콘을 냉장고로 오해했다는 농담처럼 들림
예전에는 실제로 재미있고 귀여운 하드웨어 거북이가 있었다는 맥락이 빠진 느낌임- 그 페이지에 보이는 것이 “진짜” 거북이라고 보기는 어려운 듯함
-
제목만 보고는 The Good Place의 Janet에서 따온 또 다른 AI 비서인 줄 알았는데, 실제로는 프로그래밍 언어라서 기분 좋게 놀랐음
Janet 언어는 사후세계를 헤쳐 나가도록 인간을 돕는 The Good Place의 불멸 존재 Janet에서 이름을 따왔고, 그래서 제목도 그에 맞춘 영리한 농담임
기본 정규식 엔진 대신 PEG(구문 분석 표현 문법) 을 함께 제공한다는 점도 의외였음
기본 정규식 라이브러리를 제공하는 언어라면 제대로 된 PEG도 같이 넣어주길 늘 바랐음- GNU Guile도 표준 라이브러리에 PEG 라이브러리가 있음
<https://www.gnu.org/software/guile/manual/html_node/PEG-Pars...> 참고 - Janet의
spork모듈은 일종의 libc 같은 위치이고, 정규식 하위 모듈도 있어서 설치되어 있을 가능성이 큼
Janet으로 PEG를 처음 접했지만, 강력하고 쓰기 쉽다는 점이 마음에 듦
정규식은 20년 넘게 조금씩 만졌는데도, PEG는 몇 달 만에 더 잘 이해하게 됨
Janet 커뮤니티 안에는 The Good Place 관련 콜백도 많고, 여러 서드파티 모듈이 등장인물 이름을 따서 지어졌음 - Elm도 비슷한 접근을 함
elm/regex와elm/parser가 내장된 것은 아니지만,elm/regex는 대신 elm/parser를 쓰라고 권함
이름의 유래는 마음에 들지만, Janet이라는 이름의 언어라면 제어를 우선시해야 할 의무도 있어 보임
- GNU Guile도 표준 라이브러리에 PEG 라이브러리가 있음
-
책은 읽었고 꽤 흥미롭고 장난스러운 느낌이라 Janet 학습이 훨씬 재미있어졌음
다만 언어 문서는 여러 면에서 부족했음
함수나 명령이 그냥 나열되어 있는데 예제나 설명이 없거나, 무엇을 하는지 설명이 빠진 경우가 자주 있었음
책에서는 본 기능을 문서에서 찾지 못한 경우도 많았고, 그래서 Janet을 좋아하면서도 배우기를 멈춘 이유 중 하나가 됨
상식적인 기본값이 많고 낡은 Lisp 관례를 고집하기보다 주류 언어와 닮은 점이 있다는 점은 좋음
결국 cons 리스트가 아니므로 실제로는 Lisp가 아니지만, 그건 괜찮음
문제는 언어가 충분히 문서화되어 있지 않다는 점임- 문서화되지 않았던 함수나 명령을 기억하는지 궁금함
Janet 커뮤니티에서 어느 정도 활동 중이라서, 부족한 부분을 알면 문서 개선에 기여할 수 있음 - 책을 읽지 않았다는 점만 빼면 똑같은 경험을 했음
종종 함수 시그니처만 있고 추가 설명이 전혀 없었음
폭넓은 문서를 만드는 데 시간과 노력이 많이 든다는 건 알지만, 이런 일이 너무 자주 있어서 내려놓고 바이너리로 컴파일되는 다른 Lisp를 찾기 시작함
- 문서화되지 않았던 함수나 명령을 기억하는지 궁금함
-
Janet에 잘 적응하지 못했는데, 내 문제인지는 모르겠음
오래 써본 유일한 다른 Lisp가 Emacs Lisp인데, Janet은 충분히 비슷하면서도 미묘하게 달라서 계속 사소한 실수를 하게 됨
PEG 기능은 문서상으로는 매력적이었지만 문법이 다루기 불편하고 헷갈렸음
문서는 모든 함수 설명이 있다는 의미에서는 완전했지만, 예제나 유용한 사용 정보가 부족해서 매우 간결했음
JSON 파싱 같은 기본 기능도 포함되어 있지 않은 듯했음
빠른 스크립트를 쓰려는 언어에서 JSON이나 XML 파서를 먼저 작성해야 한다면 이미 실패한 작업임
4년 전쯤의 일이니 생태계가 나아졌을 수도 있고, 스크립트 용도로 Janet을 평가한 기준 자체가 틀렸을 수도 있음
그래도 이 글을 보니 다시 설치해보고 싶어짐- JSON과 정규식 같은 것은
spork모듈에 넣어두었고, 이는 일종의 libc 같은 위치임
핵심을 가볍게 유지하려는 내부 지향이 강함
많은 프로젝트가spork에 의존하므로 초기에 설치되어 있을 가능성이 큼
문서는 괜찮은 편이지만 실용 예제에는 아직 빈틈이 있다는 데 동의함
이 부분은 https://janetdocs.com이 보완하고 있고, 각 함수별로 “커뮤니티 예제”로 연결됨
- JSON과 정규식 같은 것은
-
주제에서 벗어나지만, 원문의 다른 블로그 글 덕분에 분리형 기계식 키보드를 쓰게 됨
몇 년간 어깨와 손목 통증이 있었는데 도움이 됐고, Ian의 다른 글도 살펴보길 강력히 추천함 -
언제든
Esc를 눌러 아래쪽에 붙는 내장 REPL을 여는 방식은 좋은 결정이 아닐 수 있음
브라우저에는 이미Esc기본 단축키가 있고, Firefox는Esc를 누르면 전체화면에서 빠져나옴