Skip to content

Commit 5bfdce0

Browse files
Marat Zimnurovclaude
andcommitted
Язык научился читать файл и ходить в сеть, не перестав быть чистым
Это то, ради чего затевалось всё остальное: до сих пор на flang нельзя было ни прочитать файл, ни сходить в сеть, и это было записано недостачей в README, в docs/overview.ru.md и на витрине Rosetta. РЕШЕНИЕ ИЗ КОНТРАКТА, ДОСЛОВНО. Язык остаётся ЧИСТЫМ. `вариант «Прочитать файл» с путь равным "адрес.txt"` не читает файл — оно строит ОПИСАНИЕ действия, обычное значение обычной суммы типов. Исполняет хозяин: среда, в которую напечатан модуль. Отсюда всё остальное. НАБОР ПОРУЧЕНИЙ ЗАКРЫТ И РАВЕН ПЯТИ: прочитать файл, записать файл, запрос по сети, текущее время, случайное число. Это утверждение, а не текущее состояние: каждое поручение обязан уметь исполнить КАЖДЫЙ из восьми хозяев, включая напечатанного в C. Шестое пришлось бы либо писать восемь раз, либо объявлять «кое-где не работает» — а это уже не контракт. ЭТО НЕ МОНАДА, И ЭТО НАЗВАНО ПРЯМО. Монада ввода-вывода требует типа `Действие А`, параметрического по результату: без параметра не выразить `соединение`. Параметрических типов ещё нет, и ждать их было нельзя — без ввода-вывода язык не читает файл сегодня. Вместо монады — машина продолжений: монада прячет продолжение в замыкании; здесь продолжение — ЗНАЧЕНИЕ объявленного автором типа. Это дефункционализация монады, сделанная руками, — тот самый приём, которым PLAN.md собирается печатать функции первого класса. Что потеряно, перечислено четырьмя пунктами в cat/SPEC.md: связывание не выражение, тип результата поручения не отслеживается, продолжение лежит в джокере `любое`, законов монады нет. Когда появится полиморфизм, форма `в монаде` будет разворачиваться компилятором в ту же машину — написанное сегодня продолжит работать. ГЛАВНОЕ СВОЙСТВО — ПРОВЕРЯЕМОСТЬ БЕЗ ЭФФЕКТОВ. Описание сравнивается с ожидаемым описанием, поэтому функция с вводом-выводом проверяется ОБЫЧНЫМ примером. В examples/io/link-report.flang десять примеров, включая пути отказа, и ни один не трогает ни файла, ни сокета. Там же второе следствие чистоты: все семь функций программы, ходящей в сеть, ДОКАЗАНЫ ТОТАЛЬНЫМИ — завершается описание, а ждёт ответа хозяин. ОДНО НОВОЕ СЛОВО. `план` объявляется теми же тремя строками, что процесс: `состояние`, `начинает с`, `обрабатывает`, — потому что форма у них одна и та же, и разные слова для одинаковых строк заставили бы помнить два словаря. Занятость проверена grep-ом по всем .flang и .fts: «план» и «plan» — ноль голых вхождений. Слово «шаг» ОТВЕРГНУТО: семь голых вхождений (поле записи и переменная в core/lexer.flang, накопитель свёртки в rosetta/fibonacci.flang) — ключевое слово сломало бы два файла, к вводу-выводу отношения не имеющих. Таблица поправлена в обоих местах: src/lexer.mjs и self/lexer.flang. Имена вариантов проверялись отдельно, хоть ключевыми словами и не становятся: «Готово» занято в core/evaluate.flang, «Начало» — в self/lexer.flang, поэтому в словаре «Конец работы» и «Пока ничего». ХОЗЯИН НА NODE — НАСТОЯЩИЙ. src/host/node.mjs: node:fs, fetch, часы, mulberry32 от семени. Все эффекты языка живут ровно в этом файле, пять функций. Полномочия принадлежат хозяину и сужаются ключами (--no-net, --no-write, --in-dir …); запрещённое поручение возвращается программе откликом «Сбой», а не падением — «сеть выключили» и «сеть не ответила» обязаны идти одним путём. ЖИВОЙ ПРОГОН. flang io на examples/io/link-report.flang читает адрес.txt, идёт по адресу настоящим сокетом, пишет отчёт, печатает журнал поручений. Тот же план прогоняется поддельным хозяином из списка откликов — журналы сравнимы напрямую. ПЕЧАТЬ УЖЕ РАБОТАЕТ ВО ВСЕХ ВОСЬМИ ЦЕЛЯХ: план — это две обычные функции и три обычные суммы. Проверено сборкой: emit --target c, cc -std=c99 -Wall -Wextra -Werror -pedantic, и напечатанный C выдаёт то же описание поручения, что интерпретатор. Чего не хватает семи целям — цикла хозяина и пяти исполнителей — расписано таблицей в cat/SPEC.md, по строке на цель, с указанием, чего нет в стандартной библиотеке (HTTP в C и Rust) и что с этим делать. Словарь приписывается программе только по употреблению — программа без ввода-вывода остаётся побайтово прежней, иначе неподвижная точка сошлась бы к другому AST. В types.mjs добавлены ровно две строки: импорт и вызов; сама проверка плана живёт в io.mjs. Диагностики свои, а не общий FLANG_TYPE: FLANG_PLAN, FLANG_UNKNOWN_PLAN, FLANG_IO, FLANG_IO_LIMIT, FLANG_IO_DENIED. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6ehGNnEcrkCK1V5iMHNYi
1 parent 39f832f commit 5bfdce0

20 files changed

Lines changed: 1912 additions & 71 deletions

File tree

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,9 +1026,14 @@ attaching a solver to the verification conditions is an open task, not a feature
10261026
Higher-order work is covered by `отобразить` / `отфильтровать` / `свёртка`, which take a *body*
10271027
rather than a function. The price is that exponential objects are inexpressible, and with them
10281028
part of the category constructions.
1029-
- Effects are described, not performed. The language is pure; work with a network or a file is
1030-
meant to be a description of an action that the host executes. The I/O monad is not
1031-
implemented, so today the host does that work — as `examples/library-api/host/` shows.
1029+
- Effects are described, not performed — and this works: `вариант «Прочитать файл» с путь
1030+
равным …` builds a value, and the host executes it (`flang io`, `flang/src/host/node.mjs`).
1031+
There are five orders — read a file, write a file, make a network request, read the clock,
1032+
draw a random number — and the set is closed. There is no I/O monad, though: a monad needs
1033+
parametric polymorphism, so sequencing is expressed by a continuation machine where the
1034+
continuation is a declared value rather than a hidden closure. How that differs from a monad,
1035+
and what changes once polymorphism lands, is in `flang/cat/SPEC.md`. The execution layer
1036+
exists for one target out of eight (Node); emitting a program with a plan works for all eight.
10321037
- No dictionaries, no arrays with random access, no bitwise operations. Table-driven dynamic
10331038
programming (Coin Change, Edit Distance) does not transfer; a dictionary is a list of pairs.
10341039
- The totality analysis knows structural decrease only. A number getting smaller is not decrease,

README.ru.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,9 +1038,14 @@ node --test flang/test/core-json.test.mjs
10381038
разрешимым. Всё, ради чего обычно берут высший порядок, закрывают `отобразить` /
10391039
`отфильтровать` / `свёртка` — они принимают *тело*, а не функцию. Цена — экспоненциальные
10401040
объекты невыразимы, а вместе с ними часть категорных конструкций.
1041-
- Эффекты описываются, а не выполняются. Язык чистый; работа с сетью или файлами задумана как
1042-
построение описания действия, которое исполняет хозяин. Монада ввода-вывода не реализована,
1043-
поэтому сегодня эту работу делает хозяин — как в `examples/library-api/host/`.
1041+
- Эффекты описываются, а не выполняются, и это работает: `вариант «Прочитать файл» с путь
1042+
равным …` строит значение, а исполняет его хозяин (`flang io`, `flang/src/host/node.mjs`).
1043+
Поручений пять — чтение и запись файла, запрос по сети, время, случайное число, — набор
1044+
закрыт. Монады ввода-вывода при этом НЕТ: она требует параметрического полиморфизма, поэтому
1045+
последовательность действий выражается машиной продолжений, где продолжение объявлено
1046+
значением, а не спрятано в замыкании. Чем это отличается от монады и что изменится, когда
1047+
полиморфизм появится, — `flang/cat/SPEC.md`, раздел «Эффекты и HTTP». Слой исполнения сделан
1048+
для одной цели из восьми (Node); печать программы с планом работает во всех восьми.
10441049
- Нет словарей, нет массивов с произвольным доступом, нет побитовых операций. Табличное
10451050
динамическое программирование (Coin Change, Edit Distance) не переносится; словарь — это список
10461051
пар.

docs/overview.ru.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,14 @@ npm install -g @digitable-lol/fts
196196
задачей.
197197

198198
**Эффекты выражаются описанием, а не выполняются.** Язык чистый; работа с сетью
199-
или файлами задумана как построение описания действия, которое исполняет
200-
хозяин — среда, в которую напечатан модуль. На момент этой редакции монада
201-
ввода-вывода не реализована.
199+
или файлами — это построение описания действия, которое исполняет хозяин, среда,
200+
в которую напечатан модуль. Сделано: пять поручений (чтение и запись файла,
201+
запрос по сети, время, случайное число), объявление `план`, хозяин на Node и
202+
команда `flang io`. Функции, строящие поручения, проверяются обычными примерами
203+
и остаются тотальными — сеть в язык не входит. Монады ввода-вывода при этом нет:
204+
она требует параметрического полиморфизма, и вместо неё — машина продолжений
205+
(`flang/cat/SPEC.md`, «Чем это не монада»). Слой исполнения написан для одной
206+
цели из восьми.
202207

203208
## См. также
204209

examples/library-api/README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ examples/library-api/
3838
|---|---|---|
3939
| **FTS** (`domain/`) | тариф штрафа, право выдать книгу | это правят не программисты; у правил есть примеры, у права — сертификат |
4040
| **flang** (`lib/`, `stdlib/`) | разбор ISBN и строки запроса, отбор, сводка, печать карточки | FTS для этого слишком узок: у утилиты нет ни строк, ни списков, ни рекурсии |
41-
| **Node** (`host/`) | HTTP, маршруты, хранилище, percent-декодирование | у языка нет эффектов, монады ввода-вывода и функций первого класса |
41+
| **Node** (`host/`) | HTTP-сервер, маршруты, хранилище, percent-декодирование | сервер — не эффект, а ожидание входящих: поручения такого рода в языке нет и не планируется |
4242

4343
Граница проведена по одному правилу: **если у куска логики может быть пример —
4444
он уезжает в модель или в модуль языка**, где пример выполняется набором. На
@@ -146,8 +146,14 @@ node -e 'import("./out-js/prikladnoy_sloy.js").then(m => console.log(m.kodVeren(
146146

147147
## Где проходит граница честности
148148

149-
HTTP на flang не написан и написан быть не может. В языке нет эффектов, нет
150-
монады ввода-вывода и нет функций первого класса; `host/server.mjs` — обычный
149+
HTTP на flang не написан и написан быть не может. `host/server.mjs` — обычный
151150
модуль Node, и это не обходной путь, а устройство. Язык отвечает за то, что
152151
происходит с данными, и отвечает за это с примерами и доказанным завершением.
153152
Всё остальное — работа хозяина.
153+
154+
С 7 августа 2026 у языка появились эффекты — описанием, которое исполняет
155+
хозяин (`flang/cat/SPEC.md`, «Эффекты и HTTP»). Здесь это ничего не меняет, и
156+
причина стоит того, чтобы её назвать: набор поручений закрыт и описывает
157+
ИСХОДЯЩИЕ действия — прочитать файл, сходить по адресу. Сервер — это обратное:
158+
ожидание входящих. Поручения «слушать порт» в наборе нет и не будет, потому что
159+
исполнить его обязан был бы каждый из восьми хозяев, включая напечатанного в C.

examples/library-api/host/server.mjs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
*
77
* Что здесь есть и почему именно здесь: сокет, маршруты, коды ответов,
88
* percent-декодирование, чтение тела запроса, хранилище. Ничего из этого в
9-
* flang не выражается — у языка нет эффектов, нет монады ввода-вывода и нет
10-
* функций первого класса, а значит нет и способа написать обработчик. Это не
11-
* временное состояние и не недоделка: язык сознательно остановлен там, где
12-
* начинается внешний мир.
9+
* flang не выражается, и это не временное состояние: язык сознательно
10+
* остановлен там, где начинается внешний мир.
11+
*
12+
* У языка с 7 августа 2026 эффекты есть — описанием, которое исполняет хозяин
13+
* (`flang/cat/SPEC.md`, «Эффекты и HTTP»). Здесь это ничего не меняет, и
14+
* причина в направлении: набор поручений закрыт и описывает ИСХОДЯЩИЕ действия
15+
* («прочитать файл», «сходить по адресу»). Сервер — обратное: ожидание
16+
* входящих. Поручения «слушать порт» в наборе нет и не будет — исполнить его
17+
* обязан был бы каждый из восьми хозяев, включая напечатанного в C.
1318
*
1419
* Что здесь СОЗНАТЕЛЬНО отсутствует, хотя написать это на JavaScript было бы
1520
* на пять строк короче:

flang/PLAN.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@
8080
2. Функции первого класса ──────┴─→ 5.
8181
```
8282

83+
Стрелка «3 → 4» оказалась не блокирующей: ввод-вывод сделан ДО монады, машиной
84+
продолжений вместо связывания (см. пункт 4). Порядок от этого не сломался —
85+
монада, когда появится, встанет поверх той же машины, — но честнее показать
86+
зависимость такой, какой она вышла:
87+
88+
```
89+
1. Параметрический полиморфизм ──→ 3. Монада ──→ 4. форма «в монаде» поверх готовой машины
90+
4. Ввод-вывод (машина продолжений) ✅ ──→ 6. Отказоустойчивость
91+
```
92+
8393
**1. Параметрический полиморфизм.** Без него `Возможно` для чисел и для строк —
8494
два разных типа с разными именами конструкторов, а `M(M)` — третий, написанный
8595
руками. Из-за этого невыразима монада, и из-за этого же stdlib дублируется.
@@ -107,10 +117,25 @@
107117
разворачивается компилятором в вызовы `возврат` и `соединение`; функций первого
108118
класса ей не нужно, потому что тело известно синтаксически.
109119

110-
**4. Ввод-вывод как монада.** Язык остаётся чистым: `запросить` строит ОПИСАНИЕ
111-
действия, исполняет его хозяин. Нужен слой исполнения на каждую цель — по разу,
112-
как рантайм. Здесь же появляется то, ради чего всё: программа на flang сможет
113-
читать файл и ходить в сеть, не переставая быть проверяемой.
120+
**4. Ввод-вывод. 🟡 Сделан — но НЕ монадой.** Язык остался чистым: описание
121+
действия — обычное значение (`«Поручение»`, пять вариантов, набор закрыт), а
122+
исполняет его хозяин. Программа на flang читает файл, ходит в сеть и пишет
123+
результат, не переставая быть проверяемой: примеры на функциях с вводом-выводом
124+
сравнивают ОПИСАНИЕ с ожидаемым описанием, и все они остаются тотальными.
125+
126+
Монады здесь нет и не могло быть: она требует пункта 1 — типа `Действие А`,
127+
параметрического по результату. Ждать его не стали, потому что без
128+
ввода-вывода язык не читает файл сегодня. Вместо монады — машина продолжений:
129+
продолжение не спрятано в замыкании, а объявлено значением. Это
130+
дефункционализация монады, сделанная руками; когда появится пункт 1, форма
131+
`в монаде` будет разворачиваться компилятором в ту же машину, и написанное
132+
сегодня продолжит работать. Подробности и цена — `flang/cat/SPEC.md`, раздел
133+
«Чем это не монада».
134+
135+
Слой исполнения сделан для одной цели из восьми — Node. Печать программы с
136+
планом при этом работает во всех восьми (проверено сборкой C), потому что план —
137+
это две обычные функции и три обычные суммы; чего не хватает остальным семи,
138+
расписано таблицей в том же разделе контракта.
114139

115140
**5. Стандартная библиотека, настоящая.** Сейчас 76 функций, и они дублируются
116141
по типам. С (1) и (2) появляются свёртки, отображения и фильтры, работающие с

0 commit comments

Comments
 (0)