flang компилятор доказывает, что программа не зациклится 0.7.29 GitHub English

Приложение в браузере

«Градины» — приложение во вкладке, целиком на flang. Не стенд: показывает состояние, отвечает на действия человека, считает.

Наберите число цифрами, нажмите «пуск» — и число падает по правилу Коллатца: чётное делится пополам, нечётное умножается на три и плюс один. «Шаг» двигает на такт, «бег» считает сам, пока не остановят.

sh docs/examples/web/build.sh                                          # напечатать модуль в JavaScript
bootstrap/flang io docs/examples/web/stand.flang --max-orders 100000    # поднять стенд
# открыть http://127.0.0.1:8908/

Ни Node, ни npm, ни python3 -m http.server: печатает модуль двоичный компилятор, отдаёт страницу стенд, написанный на flang (docs/examples/web/stand.flang).

Вкладка не разбирает flang: в неё едет напечатанный модуль, 78 152 байта (sh docs/examples/web/build.sh).

Сколько чего написано

строкчтона чём
256docs/examples/web/browser-app/hailstone.flang — всё приложениеflang
395docs/examples/web/stand.flang — стенд, отдающий страницуflang
398flang/src/emit/js/flang_host_browser.js — хозяин вкладкиJavaScript
242docs/examples/web/browser-probe.sh — прогон в настоящем браузереоболочка
88docs/examples/web/browser-app/index.html — разметка; строк JavaScript в ней нольHTML

Строк JavaScript, написанных руками ради этой страницы, — ноль. Тег запуска несёт два признака и ни одного выражения:

<script type="module" src="../../flang/src/emit/js/flang_host_browser.js"
        данные-модуль="./gradiny.js"></script>

Хозяин вкладки — не «своё» приложения, а тело цели печати «js»: он один на все страницы дерева и обязан быть на языке цели, как flang/src/emit/c/*.c обязаны быть на C.

Из 256 строк flang 106 — примеры (пример, дано, ожидается), то есть больше трети файла это проверки, лежащие рядом с проверяемым. Функций 16, тотальных 16 из 16, примеров 31, упавших 0.

В хозяине без комментариев и пустых строк 218 (подсчёт grep). Прикладной логики в нём ноль: он не знает ни про Коллатца, ни про кнопки — он знает про места и события.

Толкающий цикл лёг на нынешний уклад. Правок в runPlan — ноль

Это главное измерение работы, и ответ вышел не тот, что ждали.

Цикл поручений тянущий: runPlan сам просит у программы следующее поручение. Браузер толкающий: события зовут программу. Оценка была «1 функция

одной строкой, написанной задолго до всякого браузера:

отклик = проверитьОтклик(await исполнить(поручение), поручение, план)

await и есть инверсия управления. Хозяин браузера возвращает обещание, которое разрешает addEventListener; пока план ждёт, стек пуст, вкладка жива и отзывчива, а разбудит план не программа, а человек либо часы. Прогон это подтверждает числом: «бег» довёл 27 до единицы за 111 тактов, каждый — пробуждение по часам, и «сброс» после них сработал сразу.

То есть тянущий цикл и толкающий мир — не противоположности. Тянущим цикл выглядит изнутри программы; снаружи он ровно столько же толкающий, сколько хозяин захочет.

Чего переворот всё-таки стоил — три вещи, все названные

1. Предел поручений — мера пакетной программы. Умолчание исполнителя — 10 000 поручений на прогон, и для программы, которая кончается, это ловушка на зациклившуюся. Для вкладки то же число значит «приложение умрёт после нескольких тысяч нажатий». Чинится вызывающим: хозяин вкладки зовёт ioRun с { поручений: 0 }, и ноль снимает предел (flang/src/emit/js/flang_host_browser.js, исполнитель flang/src/emit/js/flang_io.js). Правки в словаре действий не потребовалось — но умолчание для приложения неверное, и это стоит помнить.

2. Журнал runPlan — течь, растущая со скоростью действий человека. журнал копит {поручение, отклик} на каждое поручение и не отдаётся никому до конца плана, которого у приложения нет. Один полёт числа 27 — это 225 записей за сорок секунд. Вкладка, открытая на час с включённым бегом, накопила бы их сотни тысяч. У исполнителя напечатанного модуля (flang/src/emit/js/flang_io.js) журнала по умолчанию поэтому нет: он включается признаком журнал, а не выключается им. Словарь действий (flang/self/io.flang) при этом не тронут.

3. Машина продолжений ждёт РОВНО ОДНО. «Продолжение» несёт одно поле поручение, значит двух ожиданий сразу не выразить. А источников у приложения два: человек и часы. Поэтому они сведены в одно поручение — «Ждать событие» с срок равным 350 значит «разбуди на действии человека или через 350 мс, что раньше». Это не обход ограничения, а его правильное прочтение: выбор «что раньше» принадлежит хозяину, программа же называет оба условия сразу.

Чего в язык не втащено, и почему

Ни DOM, ни window, ни addEventListener, ни querySelector — ни в словаре (flang/self/io.flang), ни в спецификации, ни в одной строке .flang. Программа называет место — строку "экран", которую придумала сама, — и не знает, куда хозяин это место положит. Граница та же, что между путь и файловым дескриптором.

Поручение несёт ТЕКСТ, а не разметку. Это сознательный отказ, и вот его цена. Чтобы программа могла описывать вид (списки, таблицы, вложенность), словарю нужна четвёртая сумма — дерево разметки, — а поля встроенных сумм сегодня плоские: string, number, any. Рекурсивный именованный тип внутри встроенной суммы пришлось бы повторить в эталоне и в десяти целях печати. Поэтому вид рисует хозяин, а программа отдаёт ему текст; innerHTML в хозяине нет и не будет — разрешить разметку значило бы втащить в словарь второй язык, на котором программа не писана и который никто не проверяет. Тот же довод и с теми же числами стоит у сокращателя ссылок, и это не совпадение: у обоих приложений один хозяин.

Тотальность цела. Задача выбрана ровно за это: «Шаг градины» — тотальная функция, а завершается ли последовательность, не доказано никем в мире. И язык этого не требует: ни одна функция не повторяет шаг, повторяет его человек либо часы. Незавершаемость живёт в цикле хозяина — там же, где у службы, — а обработчик события оказался ровно таким же тотальным шагом, каким уже был обработчик запроса.

Чем проверено без рук

docs/examples/web/browser-probe.sh — в настоящем браузере, без Node и без npm. Playwright берётся из окружения, в зависимости flang не входит. Шесть сверок экрана, все побайтовые.

У кнопки в HTML есть свойство value, и оно пустая строка. Поэтому хозяин читает сначала объявленное данные-значение: при обратном порядке он получает от всякого нажатия пустоту, и приложение отвечает «непонятная клавиша «»» на каждую цифру. Подставной документ без value этого не покажет, настоящий браузер покажет: при обратном порядке падает 3 проверки из 6.

Что осталось из семи пунктов разрыва

Закрыто 5 из 7: поручения экрана (+2 вместо +4), отклики экрана (+3), хозяин браузера (250 строк), цикл событий (0 правок), согласование (6 файлов вместо 7 — их и оказалось шесть).

Не закрыто 2: