Skip to content

⚠️ Устаревший (DEPRECATED)

Данный RFC заменён RFC-024:Новая модель конкурентности.

Трёхуровневая архитектура конкурентности (L1/L2/L3), аннотации @block/@eager, автоматический DAG-анализ и другие концепции RFC-001 были удалены. Новая модель использует блоки spawn {} как единственный примитив параллелизма без аннотаций.

Документ сохранён только для исторической справки.



title: "RFC-001:Модель спавна и система обработки ошибок"

RFC-001:Модель спавна и система обработки ошибок

Источники дизайна

ДокументСвязь
async-whitepaperИсточник дизайна, теоретическая база
language-specЦелевая спецификация

Аннотация

Предлагается модель спавна для YaoXiang: синхронный синтаксис для описания логики с автоматическим конкурентным выполнением во время runtime. Ключевые механизмы: трёхуровневая архитектура конкурентности + DAG анализ зависимостей + система типов Result.

Быстрый выбор

СценарийСпособ записиОписание
Автоматический параллелизмБез аннотаций (по умолчанию)Максимальный параллелизм
Синхронное ожидание@eagerОжидание завершения зависимостей
Полная последовательность@blockБез конкурентности, для отладки
Локальная конкурентностьspawnКонкурентность внутри области видимости @block

Мотивация

Текущие модели конкурентности в мейнстрим языках имеют очевидные недостатки:

ЯзыкМодель конкурентностиПроблема
Rustasync/await + tokioРаспространение асинхронности, крутая кривая обучения
GogoroutineНет безопасности типов
PythonasyncioОграничения GIL
JavaScriptPromise/asyncСложные колбэки

Ключевое противоречие

  1. Прозрачность vs Управляемость:Полная прозрачность, но нет контроля vs Полный контроль, но непрозрачность
  2. Конкурентность vs Отладка:Конкурентные программы сложно отлаживать vs Программы с отладкой сложно делать конкурентными

Предложение

1. Модель спавна: трёхуровневая архитектура конкурентности

Пояснение:L1/L2/L3 — это ментальная модель, помогающая пользователям понять различные сценарии. Реализация использует единый механизм: автоматический DAG-анализ + контроль через аннотации.

УровеньМентальная модельСинтаксисСпособ выполненияСтепень параллелизма
L1Запрет конкурентности@blockЧисто последовательное выполнение❌ Нет
L2Конкурентность внутри @blockspawnУправляемая конкурентность в области видимости @block⚠️ Частично
L3Полная конкурентностьПо умолчанию (без аннотаций)Автоматический анализ DAG✅ Полный

L1: Синхронный режим @block

yaoxiang
main: () -> Void @block = {
    data1 = fetch_sync("api1")
    data2 = fetch_sync("api2")
    process(data1, data2)    # Строгая последовательность, без конкурентности
}

L2: Управляемая конкурентность внутри @block

yaoxiang
# spawn можно использовать только внутри функций @block
main: () -> Void @block = {
    spawn { data1 = fetch_data("api1") }
    spawn { data2 = fetch_data("api2") }
    # Ожидание завершения всех spawn (контроль стандартной библиотекой)
    process(data1, data2)
}

L3: Полная прозрачность (по умолчанию)

yaoxiang
# Не нужны аннотации, компилятор автоматически анализирует DAG
heavy_calc: (n: Int) -> Int = fibonacci(n)

auto_parallel: (n: Int) -> Int = {
    a = heavy_calc(1)    # Автоматический параллелизм
    b = heavy_calc(2)    # Автоматический параллелизм
    c = heavy_calc(3)    # Автоматический параллелизм
    a + b + c            # При запросе значения ожидаем все результаты
}

2. Полное сравнение аннотаций

ИзмерениеПо умолчанию (без аннотации)@eager@blockspawn
Способ выполненияАвтоматический DAG-анализСинхронное ожидание зависимостейЧисто последовательноКонкурентность внутри @block
Степень параллелизма✅ Полный⚠️ По порядку зависимостей❌ Нет⚠️ Частично
Построение DAG

Руководство по выбору:

  • Максимальная конкурентность → Без аннотаций (по умолчанию)
  • Требуется упорядоченность побочных эффектов → @eager
  • Отладка/новички/критический код → @block
  • Нужна конкурентность внутри @block → spawn
yaoxiang
# По умолчанию: максимальный параллелизм
calc_all: () -> Int = {
    a = heavy_calc(1)    # Автоматический параллелизм
    b = heavy_calc(2)    # Автоматический параллелизм
    a + b
}

# @eager: синхронное ожидание
calc_seq: () -> Int @eager = {
    a = heavy_calc(1)    # Синхронное выполнение
    b = heavy_calc(2)    # Синхронное выполнение
    a + b
}

# @block: чисто последовательно
calc_simple: () -> Int @block = {
    a = heavy_calc(1)    # Принудительно синхронно
    b = heavy_calc(2)    # Синхронно
    a + b
}

# spawn: конкурентность внутри @block
calc_mixed: () -> Int @block = {
    spawn { heavy_calc(1) }
    spawn { heavy_calc(2) }
    heavy_calc(3)        # Синхронно
}

3. Анализ DAG зависимостей

3.1 Ключевой принцип: выполнение снизу вверх

Код пользователя (синхронный синтаксис):
    a = fetch(url0)
    b = fetch(url1)
    print(a)

Анализ при компиляции (снизу вверх):
    print(a) требует a → зависит от fetch(url0)
    fetch(url1) никому не нужен → изолированный DAG

Планирование во время выполнения (от листьев):
    fetch(url0) → print(a)    ← цепочка зависимостей, по порядку
    fetch(url1)                ← изолированный, параллельно независимо

Ключевой инсайт: 不是"自顶向下"生成 Future,而是"自底向上"从结果反向分析依赖。

3.2 Изолированный DAG: независимый параллелизм

Основной поток: fetch(url0) → process → print
Изолированный:  fetch(url1)  ← результат никому не нужен, параллельно независимо

Планировщик: основной поток выполняется по цепочке зависимостей, изолированный использует другое ядро параллельно

3.3 Типы ресурсов и побочные эффекты

Ключевая идея: Операции с ресурсами помечаются через типы, DAG строится автоматически. Операции с одним ресурсом автоматически сериализуются, с разными ресурсами — автоматически параллелизуются.

Границы типов ресурсов——чёткое определение:

Resource types — это встроенные в компилятор типы-маркеры. Следующие типы распознаются компилятором как ресурсы:

Тип ресурсаОписаниеПоведение компилятора
FilePathПуть к файловой системеОперации с одним путём автоматически сериализуются
HttpUrlHTTP endpointОперации с одним URL автоматически сериализуются
DBUrlПодключение к БДОперации с одним подключением автоматически сериализуются
ConsoleСтандартный выводВсе операции Console автоматически сериализуются

Пользовательские типы ресурсов требуют явного маркирования:

yaoxiang
Database: Resource              # Явное маркирование как тип ресурса
query: (Database, String) -> Result(Row, Error)
# Параметр Database — это Resource, автоматически распознаётся как операция с ресурсом

Типы без маркера Resource не отслеживаются компилятором для зависимостей по ресурсам.

Правила использования:

  • Передавайте дескрипторы ресурсов через переменные, DAG автоматически управляет порядком
  • Использование литералов для одного ресурса — проблема дизайна пользователя, не ответственность языка
yaoxiang
# ✅ Правильно: передача через переменную, DAG автоматически сериализует
filename: String = "data.txt"
File.write(filename, x)
File.write(filename, y)    # DAG сериализует

# ⚠️ Ответственность пользователя: литералы
File.write("data.txt", x)
File.write("data.txt", y)  # Может выполниться параллельно, пользователь сам отвечает

3.4 Обработка бесконечных циклов

1 цикл → Непосредственно синхронное выполнение, нулевые накладные расходы на планирование
Несколько циклов → Планировщик разбивает на части и переключается, реальная конкурентность

4. Result тип и обработка ошибок

yaoxiang
Result: (T: Type, E: Type) -> Type = { ok: (T) -> Self, err: (E) -> Self }

# Оператор ? прозрачно распространяет
process: () -> Result(Data, Error) = {
    data = fetch_data()?
    processed = transform(data)?
    save(processed)?
}

5. Дизайн узлов DAG

rust
enum NodeKind {
    Task,      // Узел задачи
    Value,     // Узел значения
    Control,   // Узел потока управления
}

struct Node {
    id: NodeId,
    kind: NodeKind,
    inputs: Vec<ValueNodeId>,   // Входные зависимости
    outputs: Vec<ValueNodeId>,  // Выходные значения
    span: Span,                 // Позиция в исходном коде
}
Тип рёберСимволСемантика
DataEdgeЗависимость по данным (поток значений)
ControlEdgeЗависимость по управлению (последовательное выполнение)
SpawnEdgeТочка входа конкурентности (возможная точка параллелизма)

6. Система типов

Send → Можно безопасно передавать между потоками
Sync → Можно безопасно разделять между потоками
Arc(T) реализует Send + Sync (потокобезопасный счётчик ссылок)

Компромиссы

Преимущества

  1. Постепенное принятие: Трёхуровневая модель адаптируется к разному уровню навыков
  2. Естественный синтаксис: Синхронный код получает производительность параллелизма
  3. Безопасность при компиляции: Ограничения Send/Sync устраняют гонки данных
  4. Отлаживаемость: Граф ошибок обеспечивает чёткое представление распространения ошибок

Недостатки

  1. Кривая обучения: Требуется понимание концепции DAG зависимостей
  2. Время компиляции: Полнопрограммный DAG-анализ может быть медленным
  3. Сложность инструментария: Требуется принципиально новый отладчик и инструменты визуализации

Альтернативные решения

РешениеПочему не выбрано
Только явный async/awaitНевозможно реализовать прозрачный параллелизм
Только полностью прозрачный параллелизмПользователь теряет контроль
Go-style goroutineНет безопасности типов, невозможна проверка при компиляции
Только режим L1Отказ от ключевой ценности модели спавна

Стратегия реализации

Разделение на этапы

  1. Этап 1 (v0.1):Синхронный режим @block, базовые типы
  2. Этап 2 (v0.2):Планировщик FlowScheduler
  3. Этап 3 (v0.3):Блоки spawn, явная конкурентность
  4. Этап 4 (v0.5):Полная прозрачность L3, автоматический DAG-анализ
  5. Этап 5 (v0.6):Граф ошибок, отладчик графов
  6. Этап 6 (v1.0):Оптимизация для production

Зависимости

  • RFC-001 без внешних зависимостей (базовое ядро)
  • RFC-008(Конкурентная модель Runtime)→ Дизайн завершён
  • RFC-011(Система generics)→ Дизайн завершён

Риски

  1. Производительность DAG-анализа: Полнопрограммный анализ может быть O(n²), требуется оптимизация
  2. Отсутствие инструментария: Отладчик требует разработки с нуля
  3. Принятие пользователями: Прозрачный параллелизм требует качественной документации

Журнал решений по дизайну

РешениеОпределениеДата
Трёхуровневая архитектура конкурентностиL1/L2/L3 постепенно2025-01-05
Позиция аннотации @blockПосле типа возврата2025-01-05
Распространение ошибок DAGРаспространение вверх по рёбрам зависимостей2025-01-06
Оптимизация производительности DAGИнкрементное построение + кэширование2025-01-06
Выбор runtimeGenerics + внедрение при компиляции2025-01-06
Интерфейс узловGenerics + внедрение функций(без trait)2025-01-06
Память графа ошибокDAG строится только внутри одной функции2025-01-06
Обнаружение конфликтов ресурсовDAG-зависимость потока данных, передача пользовательских переменных2025-01-06
Система типов ресурсовМаркер Resource + автоматическая DAG-зависимость2026-01-06
Ментальная модель L1/L2/L3Три уровня абстракции, не механизм реализации2026-01-06
Аннотация @autoУдалена, дублирует поведение по умолчанию2026-05-11
Автоматический откат L1Удалён, поведение непредсказуемо2026-05-11

Приложение: Глоссарий

ТерминОпределение
Модель спавнаПарадигма конкурентности YaoXiang: синхронный синтаксис, асинхронная суть
DAGНаправленный ациклический граф, описывает зависимости вычислений
spawnУправляемая конкурентность в области видимости @block
@blockСинхронная аннотация, отключает оптимизацию конкурентности
@eagerСпешное вычисление, ожидание завершения зависимостей
ResourceМаркер типа ресурса, операции автоматически строят DAG-зависимости
Граф ошибокВизуализированный путь распространения ошибок

Библиография