EnglishРусский Map
Zig

Взгляд хаскелиста на Zig

title
Взгляд хаскелиста на Zig
type
summary
summary
Опытный хаскелист оценивает Zig по трём критериям: выразительность, мощь системы типов и длина свободного пробега. Вывод: comptime даёт почти всё без GC.
parent
zig
tags
zig, haskell, comptime, type-system, language-design
created
2026-04-30
updated
2026-04-30
lang
ru
source_updated
2026-04-30
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

Автор pure-systems-blog пишет на Haskell больше десяти лет и оценивает новые языки по трём критериям:

  1. Насколько чисто язык позволяет выразить предметную область программы (всё, что не относится к предметной области, - это шум; ручное управление памятью - классический пример).
  2. Что язык даёт для создания корректных по построению программ и насколько программируема его система типов.
  3. 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.