Перейти к содержанию

Liza: модель языка, draft 0.1

Статус: рабочее предложение. Описанные ниже правила для состояния, владения и задач ещё не реализованы и не считаются окончательно утверждённой спецификацией. Реализованное ядро описано в спецификации.

Направление

Liza должна выражать переносимые вычисления и ограничения, а не имитировать конкретный CPU. Выход по умолчанию — нативная программа. Собственный Semantic IR сохраняет логические типы, зависимости, права доступа и эффекты до понижения в машинные операции. Отсутствие указателей в исходном языке не означает, что backend не вправе использовать адреса.

Основные ориентиры: лаконичность, отсутствие обязательного GC, явность дорогостоящих операций, модульный runtime и сложные серверные системы как инженерный критерий. Максимальная эффективность на любом железе — цель, а не универсальная гарантия компилятора.

Сущности и предлагаемые инварианты

Сущность Семантика Основной инвариант
Value Неизменяемые данные без наблюдаемой идентичности Равенство определяется содержимым; физическое хранение не наблюдаемо
Transform Вычисление значений из аргументов При одинаковых входах одинаковый результат или определённый отказ
State Изменяемая сущность с идентичностью и владельцем Изменение требует исключительного права доступа
View Ограниченный доступ к части данных без владения Не переживает владельца; границы проверяются
Handle Типизированное право обращения к ресурсу Не является адресом; недействительность имеет определённый результат
Task Логическое выполнение в области родителя Дочернее выполнение не переживает область без явной передачи владения
Effect Наблюдаемое действие или ограничиваемая стоимость Контракт вызывающего допускает эффекты вызываемого

Объект-значение не имеет собственной идентичности. Идентичность вводится через State или Handle. Это рабочая гипотеза; её ещё нужно проверить на коллекциях и серверных сценариях.

Значения и локальные привязки

Математическое значение, биты и кодирование

Различаем три уровня:

Уровень Что определяет
Значение Математический смысл и допустимые операции типа
Битовое представление N бит и их интерпретацию; для знаковых целых — дополнение до двух
Внешнее кодирование Последовательность байтов протокола или файла с явными порядком байтов и форматом

Один математический результат не обязан занимать ячейку памяти: реализация может держать его в регистре, вычислить заранее или устранить хранение. N-битное представление для побитовых операций не обещает конкретный layout в памяти. Layout, выравнивание, padding и ABI задаются отдельными контрактами.

Числовое преобразование сохраняет значение либо применяет явно выбранную политику сужения. Переинтерпретация битов, если появится, будет отдельной операцией. Сериализация также не должна неявно копировать host-представление: например, значение u16 0x1234 кодируется байтами 0x12, 0x34 в big-endian и 0x34, 0x12 в little-endian независимо от устройства хоста.

Это проектная граница для будущих протоколов, FFI и памяти, а не уже доступный API сериализации. Строки, составные значения и zero-copy потребуют собственных правил кодирования и времени жизни представлений.

Логическое копирование и обновление

Для составных копируемых значений принимаем логическую семантику: b = a связывает b с тем же содержимым, не создавая неявного права изменять a через b. Компилятор может разделять физическое хранение или исключать копирование, если это сохраняет наблюдаемое поведение и контракт стоимости.

Будущий var разрешает заменить значение локальной привязки. Это не вводит изменяемый объект с идентичностью, изменяемую ссылку или разделяемое состояние. Обновление поля составного значения ещё нужно определить отдельно; синтаксис доступа к полю сам по себе не задаёт права мутации объекта.

До реализации массивов требуется контракт для трёх разных действий: логического копирования значения, временного представления данных без владения и передачи владения. Для больших буферов пока не выбраны правила допустимых неявных копий и ограничения стоимости. Скрытые аллокации или copy-on-write не считаются решением по умолчанию.

Варианты, Option и Result должны представляться без обязательной скрытой аллокации на сам вариант. Стоимость содержащихся в нём данных остаётся явной.

Liza IR описывает эти действия до выбора физического представления. Использование alloca, load, store или getelementptr в LLVM backend не превращает значения исходного языка в наблюдаемые адресуемые объекты.

Владение и память: проект правил

  1. Ресурс имеет одного владельца. move передаёт владение, старое имя после передачи недоступно. Освобождение происходит ровно один раз.
  2. Допустимы несколько read-only views либо один исключительный mutable view. Во время mutable view другие пути доступа к той же области запрещены.
  3. View не может пережить ресурс. Передача view в task допустима только при доказанном вложенном времени жизни; через suspend потребуется отдельное правило.
  4. share создаёт явно разделяемое неизменяемое владение. Подсчёт ссылок, его атомарность и стоимость не должны скрываться от ограничений медиатракта.
  5. Границы views и недействительные handles должны проверяться. Никакого UB из-за устаревшего handle; механизм поколений и восстановления ошибок ещё открыт.
  6. Разделяемая мутация возможна только через явно синхронизированные API. Порядки атомарных операций и memory model нужно специфицировать отдельно.

Эти инварианты не равны готовому borrow checker. Нужны правила вывода, примеры допустимых/недопустимых программ и проверка возможности их реализации.

Эффекты, задачи, распределённость

Систему модулей проектируем отдельно от платформенного I/O. До появления консольных и файловых операций определяем эффекты, ошибки ресурсов и порядок их освобождения. Модуль с чистыми преобразованиями не должен автоматически подключать платформенные возможности. Полная проверка эффектов развивается на системном этапе после Core.

Предполагаемые категории: state, alloc, block, io, suspend, remote. Набор эффектов распространяется по графу вызовов. pure исключает внешние эффекты, но не доказывает завершение. Отказ арифметики в текущем ядре завершает программу; нельзя удалять потенциально падающую операцию лишь потому, что её значение не нужно.

realtime в будущем ограничивает операции, а не гарантирует время исполнения на произвольной ОС. Нужны бюджет, ограничения размера данных и политика полной очереди. Локальный вызов и RPC не взаимозаменяемы: remote требует явных ошибок, тайм-аута и отмены. Репликация и консенсус принадлежат библиотекам, а язык должен помогать проверять их контракты.

Для Task ещё нужно определить отмену, join, распространение ошибок, порядок освобождения ресурсов и гарантии прогресса. Планировщик и reactor подключаются только использующим их программам.

Выходные артефакты

Сейчас: диагностируемый исходник, JSON Liza IR, LLVM IR и host executable. В дальнейшем: object, static/shared library, assembly, WASM и cross-target build. Формат IR прототипа нестабилен; это ещё не переносимый долговременный ABI.

Открытые решения

  • Какие отдельные операции арифметики должны возвращать Result? Текущая checked-арифметика сохраняет trap; её контракт не меняется неявно при расширении типов. Остальные решения — в числовой модели.
  • Какие большие значения допускают неявное копирование, как отражать его стоимость?
  • Какие формы владения выводятся без аннотаций? Что происходит через suspend?
  • Может ли pure выделять память, и как отличать логический эффект от стоимости?
  • Как задавать размещение, SIMD и аппаратные capabilities без потери переносимости?
  • Как устроить ABI и FFI без распространения небезопасных контрактов на всё ядро?
  • На каком языке писать промышленный компилятор? Python сейчас служит прототипу семантики; это не обязательство использовать его для production toolchain.