Взгляд хаскелиста на Zig
- title
- Взгляд хаскелиста на Zig
- type
- summary
- summary
- Опытный хаскелист оценивает Zig по трём критериям: выразительность, мощь системы типов и длина свободного пробега. Вывод: comptime даёт почти всё без GC.
- parent
- zig
- tags
- zig, haskell, comptime, type-system, language-design
- sources
- zig-functional-programmers
- created
- 2026-04-30
- updated
- 2026-04-30
- lang
- ru
- translation_of
- zig-functional-programmers
- source_updated
- 2026-04-30
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Автор pure-systems-blog пишет на Haskell больше десяти лет и оценивает новые языки по трём критериям:
- Насколько чисто язык позволяет выразить предметную область программы (всё, что не относится к предметной области, - это шум; ручное управление памятью - классический пример).
- Что язык даёт для создания корректных по построению программ и насколько программируема его система типов.
- mean-free-path-language - сколько строк кода можно написать, прежде чем ваше понимание программы разойдётся с её реальным поведением.
Zig показывает отличные результаты по всем трём пунктам, и в заметке подробно объясняется почему.
Критика сборки мусора
В 1995 году GC был оправданным компромиссом: скорости процессора и памяти были сопоставимы. С тех пор процессоры ускорились примерно в 10 000 раз, а скорость доступа к памяти почти не изменилась, но языки всё ещё ориентируются на железо тридцатилетней давности. Паттерн "лес указателей в кучу", к которому подталкивает GC, упирается в потолок производительности.
Автор также отмечает когнитивную цену: сборка мусора слишком легко позволяет не думать о железе. Выросло целое поколение разработчиков, которым никогда не приходилось этого делать. В итоге мы получаем раздутый и медленный софт на оборудовании, которое по сравнению с компьютерами 30-летней давности работает как суперкомпьютер. (Сравните с peril-of-laziness-lost - там Кэнтрилл высказывает похожую претензию с другой стороны: генерация кода через LLM снимает ограничение по времени человека, которое раньше вынуждало писать просто.)
Автор ссылается на аргумент Стивена Диля (Stephen Diehl): ни у кого нет стимула создавать по-настоящему новый язык. Для академиков инженерная работа - карьерное самоубийство, индустрия не готова финансировать десятилетние проекты, а у любителей нет на это ни времени, ни денег. Из-за этого вся отрасль застряла в локальном максимуме. Zig интересен тем, что у проекта Эндрю Келли (Andrew Kelley) "хватило смелости" попытаться.
Comptime как программирование системы типов
Автор хочет программирования на уровне системы типов в духе Haskell, но без платы за GC. Механизмом для этого служит comptime в Zig:
newtype- это просто структура с одним полем:struct PlayerHealth { health: u32 }.- Типы-суммы - это размеченные объединения (tagged unions). Есть даже встроенный синтаксис для optional-значений, а собственный
Maybe(T)можно реализовать какcomptime-функцию, возвращающуюunion(enum)с вариантамиvalue: Tиnothingи методомmap, который делает сопоставление с образцом по вариантам. - Тайпклассы - это
comptime-функции, которые принимают тип, проверяют наличие нужных методов (if (!@hasDecl(T, "eql")) @compileError(...)) и возвращают структуру с функциями-обёртками. Подвох в том, что полученную структуру приходится передавать явно:EqPoint.eql(a, b)вместоa == b. По сути, это ручная передача словаря тайпкласса (dictionary passing). Более громоздкий вариант с вызовомEq(Point)в месте использования можно упростить, если сам тип сгенерирует свой словарь в полеpub const Eq = EqClass(@This()).
И явная передача словарей - это главная суть, а не недостаток. Официальная философия Zig - "никакого скрытого поведения и магии на расстоянии" ("no spooky action at a distance"). Неявная диспетчеризация тайпклассов - как раз такой пример магии, от которой язык намеренно отказывается, и автор считает этот компромисс оправданным.
Случайное переоткрытие монад
В Zig 0.16 подсистему ввода-вывода переработали на основе интерфейсов. Пример из заметок к релизу:
pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const io = init.io;
var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
...
}
Автор видит здесь монаду Reader, которая несёт в себе аллокатор и интерфейс IO, - ровно та же форма, что и у IO / IO# в Haskell. Он описывает монады не как "непонятную математическую абстракцию", а как алгебраическое представление императивного программирования в виде вычислительного контекста: вы реализуете MonadCont, чтобы получить императивную семантику, или LogicT для недетерминизма с возвратом (backtracking). Суть в том, что отсутствие встроенного понятия времени поднимает потолок выразительности и оптимизации языка. Команда Zig, судя по всему, пришла к паттерну явной передачи IO независимо, что автор считает подтверждением универсальности такого подхода.
(Я не до конца согласен с такой трактовкой: передача явного параметра io - это обычное внедрение зависимостей (dependency injection), и называть её "монадой IO" - натяжка, если у вас нет do-нотации и средств композиции. Но само наблюдение верно: обе концепции явно протаскивают контекст с эффектами.)
Итог
Оценка автора:
- Выразительность: сопоставима с Haskell, меньше шума, чем в C++ или Rust для его задач.
- Программирование системы типов: comptime "как минимум близко к Haskell и проще в программировании".
- Длина свободного пробега: больше, чем в Rust, и намного больше, чем в C++. Семантика Zig его пока ни разу не удивила неприятными сюрпризами.
Он писал на Haskell 10-13 лет и теперь переключает силы на Zig.