Skip to content

Commit b247c19

Browse files
Marat Zimnurovclaude
andcommitted
Два слова контракта конкурентности уже были именами: «начальное» и «порог»
Шаг 1 модели конкурентности: поверхность, проверки, планировщик эталона. Слова из контракта проверялись `grep -rn` по .flang и .fts до того, как стать ключевыми, — и два не прошли. «начальное» — переменная в flang/core/lexer.flang, четыре места; «порог» — параметр в flang/stdlib/optional.flang. Ключевое слово запрещает одноимённую переменную, значит завести их значило бы сломать файлы, к конкурентности отношения не имеющие. Ровно это уже случилось со «символами». Поэтому начальное состояние называется занятым `начинает с`, а порог — формой из двух частей `порог отказов`; «витков», «за» и «миллисекунд» не заняты вовсе — парсер пропускает их как слова-пояснения. Проверки: обработчик обязан принимать объявленные состояние и сообщение и возвращать отклик; адресат `отправить` обязан быть объявленным процессом, а груз — того типа, который адресат объявил в `принимает`; нетотальный обработчик без запаса витков — ошибка, а не предупреждение. Планировщик эталона: очередь готовых, ящики, виртуальное время, семя. Семя решает ровно одно — выбор процесса из готовых; всё остальное определено. Отсюда «одно семя — один журнал доставок, побайтово», и это проверяется повторным прогоном после повторного разбора исходника, а не на глаз. Зависаний нет ни в одном случае: тишина даёт «покой», исчерпание запаса — FLANG_BUDGET_EXHAUSTED и остановленный процесс, бесконечная работа — «предел пробегов». Что из контракта не влезло и записано в SPEC: `породить` не сделан вовсе (описанное действие не может вернуть имя), `продолжить` сделан без поля (отклик уже несёт состояние, второе поле было бы вторым источником правды), надзор разбирается и проверяется, но стратегий не применяет, адресат обязан быть литералом. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6ehGNnEcrkCK1V5iMHNYi
1 parent 5b54c99 commit b247c19

14 files changed

Lines changed: 2252 additions & 28 deletions

File tree

flang/SPEC.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,29 @@ Pattern := { "kind": "empty" } пустой с
234234

235235
У каждого узла — необязательное `span` (`{line, column}`) для диагностик.
236236

237+
### Конкурентность
238+
239+
Контракт модели — `flang/conc/SPEC.md`. В AST она добавляет три необязательных
240+
списка верхнего уровня; появляются они только там, где соответствующие
241+
объявления в файле есть, — как `morphisms` и `legacy`.
242+
243+
```jsonc
244+
"processes": [{ "kind": "process", "name": "Счётчик", "state": "Счёт",
245+
"initial": "пустой счёт", "accepts": "Команда счёта",
246+
"handler": "шаг счёта", "budget": 100000 | null }],
247+
"supervisors": [{ "kind": "supervisor", "name": "Учёт",
248+
"watch": [{ "process": "Счётчик", "strategy": "перезапустить" }],
249+
"threshold": { "failures": 3, "window": 5000,
250+
"otherwise": "передать выше" } | null }],
251+
"runs": [{ "kind": "run", "name": "", "seed": 4172,
252+
"inbox": [{ "process": "Счётчик", "message": … }],
253+
"expected": [{ "process": "Счётчик", "state": … }] }]
254+
```
255+
256+
Программе с процессами парсер приписывает сумму `«Действие»` — словарь языка, а
257+
не объявление пользователя (`flang/src/conc.mjs`). Поэтому у такой программы в
258+
`types` есть тип, которого нет в исходнике.
259+
237260
### Постусловия функции
238261

239262
Свойства утилиты FTS («результат не больше 20 процентов от поля сумма») — это
@@ -277,6 +300,7 @@ flang/
277300
types.mjs проверка типов, вывод типов, исчерпывающность разбора
278301
totality.mjs анализ структурного убывания
279302
interpret.mjs вычисление AST
303+
conc.mjs конкурентность: словарь действий и планировщик эталона
280304
builtins.mjs строки, списки, числа
281305
factcheck.mjs встраиваемый режим: утверждения о данных
282306
emit/js.mjs печать в JavaScript
@@ -302,6 +326,10 @@ flang/
302326
| `FLANG_NOT_TOTAL` | `тотальная` без доказуемого убывания |
303327
| `FLANG_RECURSION_LIMIT` | обычная функция исчерпала лимит вызовов |
304328
| `FLANG_BUILTIN_ARGS` | неверные аргументы встроенной формы |
329+
| `FLANG_PROCESS` | объявление процесса, надзора или прогона не той формы |
330+
| `FLANG_UNKNOWN_PROCESS` | адресат `отправить`, поднадзорный или получатель в прогоне не объявлен |
331+
| `FLANG_HANDLER_NOT_TOTAL` | нетотальный обработчик процесса без запаса витков |
332+
| `FLANG_BUDGET_EXHAUSTED` | запас витков обработчика исчерпан во время прогона |
305333

306334
## 8. Встраиваемый факт-чекинг
307335

flang/conc/SPEC.md

Lines changed: 161 additions & 20 deletions
Large diffs are not rendered by default.

flang/conc/examples/budget.flang

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Запас витков: исчерпание — определённый исход, а не зависание.
2+
//
3+
// Обработчик здесь НЕ тотальный: завершение его не доказано, и доказать нечем —
4+
// «крутить» уходит вниз по числу, а число анализ завершаемости частью значения
5+
// не считает. Поэтому язык требует назвать запас; без строки «с запасом» эта
6+
// программа не собирается, и это ошибка проверки, а не предупреждение.
7+
//
8+
// Что происходит при исчерпании: сообщение отвергается, процесс падает,
9+
// состояние остаётся тем, каким было ДО пробега (обработчик чист, поэтому
10+
// половины изменения не бывает). Дальше решал бы надзор — в шаге 1 его нет,
11+
// поэтому процесс остаётся остановленным, и прогон это фиксирует.
12+
13+
модуль «Разбор пакетов»
14+
15+
объект «Разбор»
16+
«сделано»: число
17+
18+
тип «Пакет»
19+
вариант «короткий» содержит «длина»: число
20+
вариант «бесконечный»
21+
22+
объект «Отклик разбора»
23+
«состояние»: «Разбор»
24+
«действия»: список «Действие»
25+
26+
процесс «Разборщик»
27+
состояние «Разбор»
28+
начинает с «ничего не разобрано»
29+
принимает «Пакет»
30+
обрабатывает «разобрать пакет» с запасом 2000 витков
31+
32+
тотальная функция «ничего не разобрано»
33+
возвращает «Разбор»
34+
запись «Разбор» с «сделано» равным 0
35+
36+
// Обычная функция: на отрицательном входе не завершается вовсе. Ровно поэтому
37+
// она и стоит здесь — исчерпание запаса надо на чём-то показать.
38+
функция «крутить»
39+
принимает сколько: число
40+
возвращает число
41+
если сколько равен 0
42+
то 0
43+
иначе «крутить» от (сколько минус 1)
44+
45+
функция «разобрать пакет»
46+
принимает разобрано: «Разбор», сообщение: «Пакет»
47+
возвращает «Отклик разбора»
48+
разбор сообщение
49+
случай вариант «короткий» с «длина» как длина
50+
пусть потрачено равно «крутить» от длина
51+
пусть новое равно (запись «Разбор» с «сделано» равным (разобрано.«сделано» плюс 1 плюс потрачено))
52+
запись «Отклик разбора» с «состояние» равным новое и «действия» равным []
53+
случай вариант «бесконечный»
54+
пусть напрасно равно «крутить» от -1
55+
пусть новое равно (запись «Разбор» с «сделано» равным (разобрано.«сделано» плюс напрасно))
56+
запись «Отклик разбора» с «состояние» равным новое и «действия» равным []
57+
58+
// Первый пакет по силам, второй — нет. Итог определён: «сделано» равно 1, и
59+
// это состояние после первого сообщения. Второе не изменило ничего.
60+
прогон «посильный пакет, потом непосильный»
61+
семя 1
62+
дано «Разборщик» принимает (вариант «короткий» с «длина» равным 5)
63+
дано «Разборщик» принимает (вариант «бесконечный»)
64+
ожидается «Разборщик» равен (запись «Разбор» с «сделано» равным 1)

flang/conc/examples/counter.flang

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Счётчик и журнал: два процесса, которые разговаривают друг с другом.
2+
//
3+
// Самый маленький пример, на котором видно всё, ради чего затевалась модель:
4+
// состояние принадлежит одному процессу, обработчик чист и возвращает «новое
5+
// состояние плюс список действий», а действие «отправить» описывается, а не
6+
// выполняется — исполняет его планировщик.
7+
//
8+
// Оба обработчика тотальны, поэтому запас витков им не нужен: про них доказано,
9+
// что они завершатся, и отсчитывать планировщику нечего.
10+
11+
модуль «Счётчик и журнал»
12+
13+
объект «Счёт»
14+
«всего»: число
15+
16+
объект «Записи»
17+
«строки»: список строки
18+
19+
тип «Команда счёта»
20+
вариант «прибавить» содержит «сколько»: число
21+
вариант «доложить» содержит «повод»: строка
22+
23+
тип «Команда журнала»
24+
вариант «записать» содержит «текст»: строка
25+
26+
// Отклик — запись из нового состояния и списка действий. Имена полей
27+
// («состояние», «действия») — часть контракта модели, а не соглашение файла:
28+
// по ним планировщик и находит, что процесс решил.
29+
объект «Отклик счёта»
30+
«состояние»: «Счёт»
31+
«действия»: список «Действие»
32+
33+
объект «Отклик журнала»
34+
«состояние»: «Записи»
35+
«действия»: список «Действие»
36+
37+
процесс «Счётчик»
38+
состояние «Счёт»
39+
начинает с «пустой счёт»
40+
принимает «Команда счёта»
41+
обрабатывает «шаг счёта»
42+
43+
процесс «Журнал»
44+
состояние «Записи»
45+
начинает с «пустой журнал»
46+
принимает «Команда журнала»
47+
обрабатывает «шаг журнала»
48+
49+
// Надзор объявляется данными. В шаге 1 он разбирается и проверяется, но
50+
// планировщик эталона стратегий ещё не применяет — см. flang/conc/SPEC.md.
51+
надзор «Учёт»
52+
процесс «Счётчик» стратегия «перезапустить»
53+
процесс «Журнал» стратегия «остановить»
54+
порог отказов 3 за 5000 миллисекунд иначе «передать выше»
55+
56+
тотальная функция «пустой счёт»
57+
возвращает «Счёт»
58+
запись «Счёт» с «всего» равным 0
59+
60+
тотальная функция «пустой журнал»
61+
возвращает «Записи»
62+
запись «Записи» с «строки» равным пустой список
63+
64+
// Обработчик — обычная функция языка: чистая, тотальная, проверяемая примерами.
65+
тотальная функция «шаг счёта»
66+
принимает текущее: «Счёт», сообщение: «Команда счёта»
67+
возвращает «Отклик счёта»
68+
пример «прибавление меняет состояние и ничего не шлёт»
69+
дано текущее равно (запись «Счёт» с «всего» равным 1)
70+
дано сообщение равно (вариант «прибавить» с «сколько» равным 2)
71+
ожидается (запись «Отклик счёта» с «состояние» равным (запись «Счёт» с «всего» равным 3) и «действия» равным [])
72+
разбор сообщение
73+
случай вариант «прибавить» с «сколько» как сколько
74+
пусть новое равно (запись «Счёт» с «всего» равным (текущее.«всего» плюс сколько))
75+
запись «Отклик счёта» с «состояние» равным новое и «действия» равным []
76+
случай вариант «доложить» с «повод» как повод
77+
пусть строчка равно (соединить повод с (к строке текущее.«всего»))
78+
пусть доклад равно (вариант «отправить» с «кому» равным "Журнал" и «что» равным (вариант «записать» с «текст» равным строчка))
79+
запись «Отклик счёта» с «состояние» равным текущее и «действия» равным [доклад]
80+
81+
тотальная функция «шаг журнала»
82+
принимает записанное: «Записи», сообщение: «Команда журнала»
83+
возвращает «Отклик журнала»
84+
пример «строка копится»
85+
дано записанное равно (запись «Записи» с «строки» равным [])
86+
дано сообщение равно (вариант «записать» с «текст» равным "итог: 3")
87+
ожидается (запись «Отклик журнала» с «состояние» равным (запись «Записи» с «строки» равным ["итог: 3"]) и «действия» равным [])
88+
разбор сообщение
89+
случай вариант «записать» с «текст» как строчка
90+
пусть новое равно (запись «Записи» с «строки» равным (добавить строчка к записанное.«строки»))
91+
запись «Отклик журнала» с «состояние» равным новое и «действия» равным []
92+
93+
// Пример конкурентной программы — это семя, входные сообщения и ожидаемый итог.
94+
// Итог здесь от чередования не зависит: сложение коммутативно, а доклад приходит
95+
// после прибавлений, потому что все три сообщения лежат в одном ящике и
96+
// разбираются по очереди. Программа, итог которой зависел бы от чередования,
97+
// была бы неверна — и это её свойство, а не недостаток проверки.
98+
прогон «два прибавления и доклад»
99+
семя 4172
100+
дано «Счётчик» принимает (вариант «прибавить» с «сколько» равным 2)
101+
дано «Счётчик» принимает (вариант «прибавить» с «сколько» равным 3)
102+
дано «Счётчик» принимает (вариант «доложить» с «повод» равным "итог: ")
103+
ожидается «Счётчик» равен (запись «Счёт» с «всего» равным 5)
104+
ожидается «Журнал» равен (запись «Записи» с «строки» равным ["итог: 5"])
105+
106+
прогон «то же самое на другом семени»
107+
семя 99
108+
дано «Счётчик» принимает (вариант «прибавить» с «сколько» равным 2)
109+
дано «Счётчик» принимает (вариант «прибавить» с «сколько» равным 3)
110+
дано «Счётчик» принимает (вариант «доложить» с «повод» равным "итог: ")
111+
ожидается «Счётчик» равен (запись «Счёт» с «всего» равным 5)
112+
ожидается «Журнал» равен (запись «Записи» с «строки» равным ["итог: 5"])

0 commit comments

Comments
 (0)