Pull to refresh
16K+
2
Doniyor Botirov@botiroff

User

9
Rating
2
Subscribers
Send message

По asm.js вы правы, и я сказал неточно: в V8 AOT для asm.js не было никогда — там он шёл обычным JIT. AOT делал Firefox в OdinMonkey: модуль с “use asm” валидировался и компилировался до исполнения. Отсюда и разночтение.

А вот «у AOT аннотация бы растворилась» — это как раз то, что AOT с ней и делает. Валидатор по |0 доказывает, что значение помещается в int32, и генерирует целочисленную арифметику; сама аннотация в машинный код не попадает именно потому, что её потребили на этапе компиляции. Растворение — признак того, что она сработала, а не того, что её там не было.

И вы точно описали мотив: сужали намеренно, ради памяти и скорости, компилируя из C/C++. Это ровно моя мысль, только сказанная лучше — |0 был инструментом производительности в статически типизированном подмножестве, а не типом в языке. Поэтому перенести его в динамический интерпретатор нельзя: аннотировать нечего, а диапазон сузится.

Про GC вы правы, и это самый сильный пункт: модель языка я действительно не описал. Отвечу конкретно, потому что от неё всё и зависит.

Без сборщика Sable не обойдётся, и вот почему — три свойства, каждое из которых у него уже есть:

  1. Замыкание переживает кадр, в котором создано fn счётчик() { let n = 0; return fn() { n += 1; retur

  2. Взаимные ссылки — обычная структура, не экзотик struct Узел { имя, сосед = nil } a.сосед = b; b.сосед = a

  3. Цикл замыкается и через функцию h.дай = fn() { return h }

Первое убивает стековое размещение: окружение переживает вызов, и время его жизни статически неизвестно. Второе и третье убивают подсчёт ссылок — цикл никогда не дойдёт до нуля. Остаили подсчёт ссылок с отдельным сборщиком циклов, чтопо сложности то же самое.

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

Путь Blazor вы назвали верно: собственный сборщик в леалистично и не требует ждать wasm-gc — просто этоотдельная работа размером с сам интерпретатор.

Про модель языка напишу отдельно — вопрос справедливый, и в статье про тесты ей места не нашлось. Спасибо за разбор, он оказался полезнее, чем половина того, что я закладывал в текст

Спасибо, по первому пункту разберу подробно — там смешаны три разные вещи.

number|0 — это не целочисленный тип, а приведение к int32. И для языка с числами-double это шаг назад, а не вперёд: double даёт точные целые до 9007199254740991, а |0 — до 2147483647. На большем оно не «магически превращается», а ломается:

231 | 0 → -2147483648 (253 - 1) | 0 → -1

В asm.js |0 работал не как тип, а как аннотация для AOT-компилятора: разметка статически типизированного подмножества, чтобы движок скомпилировал его заранее. В динамическом интерпретаторе аннотировать нечего — значение всё равно останется боксированным, а диапазон сузится в четыре миллиона раз.

Uint8Array — это буфер байтов, а не скалярный числовов языке целые» отношения не имеет.

BigInt — единственный реальный кандидат, и я его расс, и вот чем платят:

Number: 20 мс BigInt: 110 мс → в 5,5 раза медленн 1n + 1 → TypeError: Cannot mix BigInt and other types 7n / 2n → 3n (а 7 / 2 = 3.5) JSON.stringify(1n) → TypeError

То есть это не «добавить тип», а решить заново, что в языке значит /, что происходит при смешивании, и как это сериализуетсцелочисленный тип у меня в открытых вопросах, а не в атье он честно назван отсутствующим, а не «почтиесть».
Про мотивацию — вопрос справедливый. Язык здесь не цель, а предмет: статья про систему проверок, которая позволила переписацеликом и не сдвинуть ни одного эталона. Но принцип уательный — ошибаться громко и рано, впротивоположность JavaScript:

  • выход за границы списка и отсутствующий ключ — ошибка, а не nil; - переполнение — ошибка: ни бесконечности, ни NaN в я

  • "сумма: " + 5 — ошибка, а не склейка; - у каждой ошибки позиция, строка исходника с кареткоа про опечатку. Молчаливый nil и тихий inf — источник багов, которые троен так, чтобы они всплывали в момент написания. Про запуск — согласен, зависимость от Node реальна. Эсборки, нет зависимостей в рантайме, весь тулчейн —node src/cli.ts file.sable. Проект в первую очередь читают, а не устанавливают, и здесь важнее, чтобы между исходником и застояло ничего. wasm со своим рантаймом — это другой проект: понадобира, либо ждать wasm-gc. Разумный путь, если цельсменится на «раздавать бинарник». Self-hosted — честно: пока нет. Чтобы компилятор написать на самом Sable, языку нужны вещи, которых у него нет на 0.2. Это классическая веха, и она интереснее wasm, но сначала работа со строками, иначе получится компилятор,который стыдно показать.

Попробовать без установки, если интересно: https://botiroff-d.github.io/sable/

Отдельное состояние «я не знаю» вместо схлопывания в успех или ошибку — то место, где обычно и ломаются такие контуры. Соблазн приравнять неизвестность к ошибке огромный, потому что так короче код, и цена всплывает позже: повтор поверх уже выполненной необратимой операции.

Из практики добавлю два вопроса, которые стоит задать такому состоянию. Первый: у него есть граница по времени? Пока не сказано, за сколько неопределённость обязана разрешиться и что происходит, если архив накопителя недоступен дольше этого срока, состояние тихо превращается в свалку, которую никто не разбирает. Второй: кто владелец очереди неразрешённых? Технически они не мешают, поэтому мониторинг их не показывает, а бизнес-смысл у каждой записи есть.

И про тестирование этого контура. Такие вещи бесполезно ловить нагрузкой: нужный отказ должен случиться ровно между «команда отправлена» и «ответ получен», и на настоящем железе это окно почти не поймать. Обычно помогает вынести всё, что связано со временем и вводом-выводом, за границу логики, а в тестах подменять их управляемым источником: тогда обрыв ставится ровно в нужную точку, а не подкарауливается.

Хорошо разложено, особенно мысль про то, что восстановление зависимости и восстановление системы — разные события: 12:00 сервис здоров, 12:03 разобрана очередь.

Добавлю то, чего мне не хватило в разделе про fault injection, — воспроизводимость. Хаос-эксперимент ловит нарушение инварианта один раз, а дальше начинается самое дорогое: попытка повторить. Если задержки, потери и порядок доставки берутся из настоящей сети, конкретное расписание не повторяется, и находка превращается в «мы такое однажды видели». Практический выход — сделать источник недетерминизма своим: время, таймеры, порядок готовых колбэков и решения «доставить / потерять / продублировать» брать из генератора с сидом. Тогда упавший прогон адресуется одним числом, кладётся в регрессию и воспроизводится на чужой машине.

И второе, ближе к вашему списку инвариантов. У набора инвариантов та же беда, что у любых тестов: если он всегда зелёный, по цвету не отличить «ловит» от «не смотрит». Дешёвая проверка — временно выключить в системе правило, которое эти инварианты обязаны защищать (например, атомарность проверки ключа идемпотентности), и потребовать, чтобы тесты покраснели с конкретным именем нарушенного инварианта, а не просто упали. Если не покраснели — проверяется не то, что кажется.

Information

Rating
894-th
Location
Worland, Wyoming, США
Date of birth
Registered
Activity

Specialization

Фулстек разработчик, Инженер встраиваемых систем
Ведущий
Английский язык
Python
Git
Базы данных
Redis
PostgreSQL
Docker