Зачем Janet, лисп на один мегабайт
открытый урокЭтот раздел читается без входа. Войди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти
Зачем Janet, лисп на один мегабайт
Открываем раздел про Janet. Это маленький динамический лисп, который весь помещается в один исполняемый файл размером около мегабайта и зависит ровно от одной библиотеки, libc. Интересен он не скобками, а редкой комбинацией инженерных решений. Урок ставит язык на карту: где он выигрывает, где проигрывает и чем отличается от соседей по семейству.
Сцена
Ты пишешь bash-скрипт. Сначала это три строки. Потом появляется разбор аргументов, потом вложенный цикл, потом попытка распарсить JSON через sed. В этот момент ты тянешься за Python. И почти сразу упираешься: на целевой машине не та версия, pip install тащит дерево зависимостей, а сам интерпретатор весит десятки мегабайт и стартует сорок миллисекунд, что заметно, если скрипт вызывается в цикле.
Есть и обратная задача. У тебя программа на C (движок, синтезатор, редактор, демон), и ей нужен скриптовый слой. Тащить внутрь Python нельзя, Lua можно, но у Lua бедная стандартная библиотека.
Janet целится ровно в эти две дырки. Его автор, Кэлвин Роуз, собрал язык вокруг ограничения: всё должно помещаться в один бинарник без внешних зависимостей.
Четыре свойства, которые закрывают обе дырки
Он крошечный. Интерпретатор со стандартной библиотекой это один файл около мегабайта, и стартует он за миллисекунды. Единственная зависимость, libc, стандартная библиотека C, которая уже есть в любой системе. Это ответ на первую дырку из сцены: скрипт вместе с интерпретатором приносишь на целевую машину одним файлом, и никакой «не той версии» там не бывает.
Он встраивается. Исходники собираются в амальгамацию: один janet.c и один janet.h. Кладёшь два файла рядом со своим кодом, и у твоей программы появился скриптовый язык. Вторая дырка из сцены закрывается ровно этим ходом.
У него батарейки в коробке. В стандартную поставку входят: PEG-парсер как полноценная замена регулярных выражений (ему посвящён 31-janet/10 · PEG: грамматики вместо регулярок), событийный цикл с зелёными потоками (31-janet/13 · Событийный цикл, каналы и таймауты), сетевой стек, работа с подпроцессами, сериализация значений в байты и обратно. Всё доступно сразу: тот самый pip install из сцены здесь не нужен вовсе.
У него один примитив конкурентности вместо трёх. Файберы служат одновременно генераторами, механизмом обработки ошибок и основой асинхронного ввода-вывода. Представь функцию, которую можно поставить на паузу, а потом продолжить с того же места со всеми её локальными переменными: это и есть файбер. Как из одного такого примитива вырастают три подсистемы, разберём в 31-janet/12 · Файберы: один примитив вместо трёх подсистем.
Вот как размер и старт выглядят рядом с привычными рантаймами:
Второй график важнее первого. Пока скрипт запускается один раз, старт в сорок миллисекунд никого не волнует. Но git-хук, cron-задача или обработчик в цикле вызывают его сотни раз, и тогда время старта становится основной статьёй расходов.
Ниша: где Janet действительно уместен
Janet не претендует на место Python или Go. Он хорош там, где важны компактность и скорость запуска.
| Задача | Почему Janet | Чем обычно решают |
|---|---|---|
| Скрипт, переросший shell | стартует за миллисекунды, работает с процессами не хуже shell, но со структурами данных вместо строк | Python, Perl |
| Скриптовый слой в программе на C | два файла рядом с кодом, без системы сборки | Lua |
| Разбор текста: конфиги, логи, мини-языки | PEG в стандартной поставке | регулярки, ANTLR |
| Творческое программирование | REPL (интерактивная консоль языка), живая перезагрузка, быстрая обратная связь | Processing, Lua |
| Небольшой веб-сервис, где деплой важнее пиковой нагрузки | один бинарник вместе с шаблонами и фоновыми задачами: скопировал файл и запустил | Rails, Django, Node |
У каждой строки этой таблицы есть живые подтверждения, и не по одному. Пройдёмся по полкам.
Что уже написано на Janet
Честный вопрос к любому маленькому языку: а что на нём есть, кроме туториалов и списков дел? У Janet ответ содержательнее, чем можно ждать от сообщества такого размера. Каждый проект ниже можно открыть и полистать.
Настоящие приложения. jwno, тайловый оконный менеджер для Windows 10 и 11. Это не поделка на выходные: его конфиг это программа на Janet с полным доступом к API менеджера, то есть язык работает сразу и реализацией, и слоем настройки. Freja, графический редактор, который перезагружает собственный код прямо во время работы. Joule, терминальный текстовый редактор. И janet-lsp, языковой сервер для Janet, сам написанный на Janet.
Веб. Joy, full-stack фреймворк с роутингом, миграциями и шаблонами. Mendoza, генератор статических сайтов от автора языка: janet-lang.org собран именно им. Bagatto, ещё один генератор, где вся конфигурация это структуры данных Janet. Я и сам сейчас строю на Janet веб-фреймворк void: серверный рендеринг с HTMX, база, очередь задач, авторизация и метрики в одном бинарнике меньше пяти мегабайт. Язык выбран не из любви к скобкам: событийный цикл и файберы закрывают конкурентность, а через REPL можно зайти в работающий процесс и чинить его изнутри.
Системные инструменты. Hermes, пакетный менеджер в духе Nix, где каждый пакет описан кодом на Janet. janet-sh, библиотека, после которой вызовы внешних процессов выглядят почти так же коротко, как в shell. kris кросс-компилирует Janet-проекты под чужие платформы через тулчейн Zig.
Графика и встраивание. Про Bauble, браузерную песочницу для генеративной 3D-графики, важна одна деталь: это Janet, скомпилированный в WebAssembly, весь интерпретатор живёт прямо в браузере. jaylib даёт биндинги к Raylib для игр и визуализаций. А фэнтези-консоль TIC-80 включает Janet в список встроенных языков наравне с Lua: большой чужой проект на C принял Janet как скриптовый слой. Как повторить этот трюк в своей программе, разберём в уроке про встраивание.
Инфраструктура разработки. spork, официальная библиотека-спутник: форматтер, сетевой REPL, менеджер проектов. Judge, тестовый фреймворк от автора книги Janet for Mortals: пишешь выражение, запускаешь, и Judge сам вписывает результат в исходник теста; к этой идее вернёмся в уроке про тестирование. Биндинги к SQLite, PostgreSQL и Redis закрывают базовую работу с хранилищами.
Почти всё из списка написано одним или двумя людьми, и это не слабость, а следствие устройства языка. Толстая стандартная библиотека снимает потребность в сотне мелких пакетов, а дешёвое встраивание и FFI позволяют не переписывать мир: нужна библиотека из мира C, берёшь и подключаешь. Экосистема Janet маленькая, но плотная: проекты выше не обёртки над обёртками, а законченные вещи.
Где Janet не лучший выбор
Скажу честно и сразу. Большой команде на долгом проекте почти наверняка нужнее статическая типизация, лёгкий найм и готовые SDK под каждый внешний сервис. Janet это маленький язык с маленьким сообществом, и это осознанный компромисс: части готового не будет, придётся писать самому. Компромисс смягчается тем, что писать обычно недолго, батарейки в языке и дешёвый FFI сильно снижают цену недостающей библиотеки. Но если тебе нужен зрелый ORM, выбор из трёх веб-фреймворков и StackOverflow на любой вопрос, смотри в сторону раздела Node.js или Go.
Чем Janet отличается от других лиспов
Если ты уже писал на лиспах, зафиксируй расхождения сразу, иначе привычки будут подводить.
От Clojure
Синтаксис похож настолько, что код на Clojure часто выглядит почти компилируемым, и это главная ловушка.
| Clojure | Janet | |
|---|---|---|
| Основа данных | неизменяемая, персистентная | две параллельные ветки: изменяемая и неизменяемая |
(:ключ словарь) | достаёт значение | вызывает метод, разберём в уроке про прототипы |
| Платформа | JVM, старт около секунды | нативный код, старт около пяти миллисекунд |
| Комментарий | ; | # |
| Разворачивание списка в аргументы | (apply + xs) | (+ ;xs) |
Первое расхождение из таблицы стоит запомнить отдельно: в Clojure (:key словарь) это идиоматичный способ достать значение, в Janet эта запись означает совсем другое и на обычном словаре ведёт себя неожиданно.
От Scheme и Common Lisp
Нет макросов чтения, нет продолжений, нет CLOS. Зато есть встроенный PEG, событийный цикл и модульная система, которая работает с файлами, а не с пакетами внутри образа.
От Lua
Ближайший конкурент по нише встраивания. Janet даёт макросы, богаче набор типов (кортежи, структуры, буферы, ключевые слова), а в его стандартной поставке есть PEG, событийный цикл и сеть: у Lua из этих трёх нет ничего из коробки. Lua даёт более зрелую экосистему и ещё меньший размер.
Как устроен раздел
Раздел рассчитан на программиста, который уже писал на чём-нибудь, не обязательно на лиспе. Скобки объясняются с нуля, циклы и функции нет.
- Уроки 2-5, фундамент: синтаксис, литералы, коллекции, модель данных. Пропускать нельзя.
- Уроки 6-9, как код превращается в исполнение и как этим управлять из макросов.
- Уроки 10-15, рабочие инструменты: PEG, файберы, событийный цикл, управление потоком.
- Уроки 16-18, организация программ: модули, пакеты, полиморфизм.
- Уроки 19-20, граница с C: FFI и встраивание. Требуют компилятора C, при желании можно отложить.
- Уроки 21-26, тесты, скриптинг, макросы, которые пишут определения, и сквозной проект.
Держи REPL открытым. Janet приятнее изучать в диалоге, чем по тексту. Открой терминал с запущенным janet и проверяй каждое утверждение сам. Встроенная команда (doc имя) часто отвечает быстрее, чем поиск по документации. Как это делать, разбираем в 31-janet/02 · Установка и REPL: разговор вместо чтения.
Что запомнить
- Janet это один бинарник около мегабайта с единственной зависимостью libc.
- Он встраивается в программу на C двумя файлами, без системы сборки.
- PEG, событийный цикл и работа с процессами лежат в стандартной поставке.
- Файберы это один примитив, из которого вырастают генераторы, обработка ошибок и асинхронный ввод-вывод.
- Ниша: скрипты переросшие shell, скриптовый слой в нативных программах, разбор текста, творческое программирование, небольшие веб-сервисы одним бинарником.
- Экосистема маленькая, но плотная: оконный менеджер jwno, редактор Freja, фреймворк Joy, пакетный менеджер Hermes и генератор сайтов Mendoza написаны на Janet, и это законченные работающие вещи, а не демо.
- Не ниша: большая команда, долгий проект, потребность в готовых SDK на каждый чих.
Упражнения
Дальше
Следующий урок практический: ставим Janet и jpm, заходим в REPL и заводим привычку разговаривать с языком, а не читать про него. Держи терминал под рукой.
домашка