Заинтересовал тезис доклада: “Цена типизации. Типизация обходится дорого”, начал слушать. Вначале всё было неплохо, но с середины 10 минуты начало потихонечку бомбить, и чем дальше, тем больше. В конце концов я не смог не прокомментировать услышанное. Далее - жирным цитаты, а следом мои комментарии.

Тот самый доклад: https://www.youtube.com/watch?v=6WfUDHlhO8s

00:00 “бла-бла-бла, вот мы в Яндексе…“

01:10 “Ликбез по типизации”

  • Вводная для тех, кто не особо в курсе различия между видами типизации

07:20 “Приверженцы строгой типизации могут часами рассказывать о плюсах … какие-то плюсы справедливы, какие-то выдуманные … мы не должны использовать технологии только потому, что все их используют … Посмотрим, какие плюсы нам обещает типизация”

08:00 “Проще рефакторинг… это правда. Подсказки в ИДЕ, это тоже правда. Любой достойный редактор имеет подсказки, но … без системы типов подсказки будут неидеальны. …замена документации. Это тоже правда. Меньше юнит-тестов. Это тоже правда. Быстрое обнаружение ошибок, опечаток … опечатаешься в названии функции и об этом узнаешь только во время запуска… Любой достойный линтер уловит эти ошибки … Все представляют, как это работает.”

  • Как бы нет. Линтер не в состоянии узнать, есть ли у принимаемого объекта в качестве параметра функции свойство crap или метод clon(), или это просто опечатки, и правильно нужно было писать crab и clown(). Ему просто неоткуда взять эту информацию. Анализировать весь ворох исходников - можно, но линтер никогда не сможет быть уверен, что этот набор окончателен, и кто-то не вызовет функцию с объектом, в котором, натурально, будет свойство crap.

10:20 “Код будет работать быстрее … это тоже почти правда … скажем V8 пытается достичь максимальной производительности с помощью … оптимизации. Если он видит, что в переменной лежит тип, и он не меняется … то он может оптимизировать всё это дело по памяти. Понятное дело, что это всё эвристика … но если мы не жестим с типами и не держим в массивах разнотипные данные то … программа будет работать … быстрее”

  • Оставим слово “почти”, но вот про V8 не могу не отметить, что он использует вовсе не эвристику, а статистику. В нём работают две машины яваскрипта одновременно - интерпретатор и JIT-компилятор, и любые типы для V8 строгие, до тех пор, пока он не столкнётся с несоответствием. Тогда он выбросит скомпилированный код и откатится на интерпретацию. Ну, и оптимизация будет не по памяти, а по скорости. Ибо весь смысл не в упаковке данных, а в скорости работы, для чего JIT-компилятор и нужен. “Не жестим с типами” - надо пояснить, что в V8 любое состояние объекта - это отдельный тип. Если в процессе инициализации объекта, скажем, пропустить одно поле, то это будет, с точки зрения V8, новый тип. С одной стороны, вроде, “не жестили”, а с другой - получили в массиве разнотипные данные и программа будет работать в “медленном” варианте.

11:20 “Самый популярный миф … у нас будет меньше багов …“

  • Холиворная тема. Сама методика измерения крайне спорная - количество открытых гитхаб-issues на строки кода, в проектах на языках с разным подходом к типизации. Эта методика может показывать вообще, что угодно. Например, что на C++ больше масштабных проектов, которые живут дольше, чем JS, они более сложные и поэтому там тикетов больше. Что полно домашних проектов на Руби и Яваскрипте, в которых никто, разумеется, не будет заводить репорты про типизацию, а будет про реализацию новых фичей. В то время, как C++ для домашних проектов, например, будут выбирать очень редко. Упоминание про статистику - вообще прекрасно. “Так работает статистика”. Ага, собрали статистику с данных, непонятно, как связанных между собой, не увидели корреляции, и решили, что типизация не влияет на корректность программ. Ну, ок.

11:50 “Собственно, корреляции нет … Типизация не творит чудеса, она не уменьшает количество багов … “

  • Пока без комментариев. Ниже поясню.

12:10 “Корректность типов не гарантирует корректность программы”

  • Да, это так, но ровно также это значит, что в программе просто не будет встречаться целого класса ошибок. Да, ошибок может быть не меньше, но они будут более высокоуровневые, а не тупые ошибки типами.

11:50 “Исследование … сотни тысяч репозиториев на Питоне … всего 2% багов связаны с типами”

  • Вот совсем некорректный пример в докладе про Яваскрипт, так как в Пайтоне типизация строгая, и если что не так - то рантайм упадёт, и ошибка обнаружится достаточно рано. То есть, что код с багом типизации окажется в репозитории на Пайтоне намного менее вероятно, чем в репозитории с Яваскриптом.

Начинается самая мякотка. Секция “Это не бесплатно”:

15:20 “Типизированный Яваскрипт - это не стандарт”

  • Да, не стандарт, но это реально надмножество. Ну, и приводить в пример приватные члены в JS - это как бы дно, потому, что они никому не нравятся, народ не хочет ими пользоваться в таком виде, и до сих пор (прошёл почти год со стадии выкатывания их в Стейдж-3), на гитхабе идут войны, непонимания, и указывается, что предложенное поведение идёт вразрез со всем предыдущим опытом языка.

16:50 “Это глючно”

  • Офигенная характеристика. Я, честно говоря, теряюсь, что значит такая семантика: test`zzz ${name} xxx`. UPD: Почитал. Это возможность вызова обработчика с заданным шаблоном из которого можно руками собрать результат. Прикольно. Ну и да, в TS действительно ошибка. https://github.com/microsoft/TypeScript/issues/27460 Что с этим делать - вопрос интересный. Особненно с учётом того, что в Бабеле с этим кодом проблема тоже есть, но проявляется несколько по-другому.

“Вы ничего не можете сделать … вряд ли вы будете форкать”

  • Неправильный аргумент. Я смотрю, что можно сделать, завожу багу в гитхабе и пишу альтернативный вариант. Иной раз приходилось писать хаки и откатываться до any, когда TS не мог вывести нужный мне тип. Но потихоньку Тайпскрипт улучшается и хаки из кода убираем.

Отмечу, что существует такая штука, как Реакт, у которого свой язык описания, который не следует стандарту языка Яваскрипта, транспилируется, в нём есть (наверняка) ошибки, он слабо и динамически типизирован, но к нему претензий нет. А есть к Тайпскрипту. Это потому, что в нём типизация сильная и статическая? Или всё озвученное относится и к Реакту и им тоже не надо пользоваться?

17:45 “Время разработки”

  • Разумеется, время разработки с типами будет дольше. Но и качество проектирования будет лучше, так как разработчику приходится изначально думать про ограничения и оценивать, что уложится в систему типов, а что нет. Ну и, кроме того, тут есть ещё такая штука, как привычность - типизированные языки сложнее, поэтому в описанном в видео эксперименте чувакам пришлось ещё и в более сложный синтаксис языка с типами вникать. Если взять Яваскрипт и Тайпскрипт и написать мелкую программку, я почти уверен, что Яваскрипт выиграет по скорости разработки. Однако, если придётся писать программу побольше, скажем, в 5-10 тысяч строк, то вот тут у меня появляется ожидание, что решение на Тайпскрипте будет лучше спроектировано, будет с меньшим количеством ошибок и проще в поддержке, что непременно окупится если не на этапе разработки, до в толгосрочной перспективе уже точно. И вероятно, что ещё и будет быстрее работать, из-за специфики современных Яваскрипт-движков.

18:40 “Надёжность … что же делать на сервере … транспилировать … ненадёжно”

  • Странные аргументы, потому, что Бабель, например, транспайлер, но к нему претензий нет. И повторю вопрос про Реакт. Который к тому же и для бекенда умеет компилироваться. Но Реакта не боимся, а Тайпскрипта - боимся. Странно.

19:40 “Синтаксический шум”

  • На картинке как раз пример из Реакта, из-за которого я писал свою типизированную обёртку, потому, что подсказки типов для Редакса/Мобикса написаны недостаточно хорошо, поэтому в приведённом коде жёсткие приведения типов: {...props as Props}, что сводит на нет всю пользу от типизации. Если доделать тайпхинты или использовать Тайпскрипт-обёртку над чистым JS-кодом, ненужный шум с этой картинки уйдёт, останутся только критически необходимые декларации в первых двух строчках.

19:50 “Типизировать - это сложно … Дальше начинается программирование на типах … от такого кода пахнет элитностью…“

  • Ну, а что делать… Зато такие сложные части прекрасно уезжают в библиотеки, а типичные прикладные разработчики просто используют готовые конструкции, особо не вникая, как это работает под капотом. Собственно, та же проблема и с C++, скажем - есть некоторая группа людей, которая отказывается вникать, как работает метапрограммирование на шаблонах. Но да и не надо им это значит. Пусть пишут себе, а метапрограммирование пусть будет в библиотеках.

20:30 “Сохранять вещи насколько простыми, насколько возможно”…

  • Крайне спорный аргумент. Тут приходится выбирать - или увеличение удобства ценой сложности, или рискуешь застрять в прошлом. Вся индустрия программного обеспечения движется в сторону усложнения, а тут такие заявления, давайте оставим язык программирования простым. Ну, ок, давайте. Да здравствует ES3! Идите в жопу, переусложнённые ES6 и их последователи! Придумали, понимаешь, всякие промисы, эвейты, генераторы и прочие символы… На них писать сложно! Давайте останемся в уютных 90-х.

21:00 “Аннотации типов очевидно делают код трудным для чтения и поддержания”

  • Щито??? Очевидно? Очевидно да, если не умеешь в типизацию. Но после небольшой практики типизация начинает не мешать, а помогать. Кроме того, если это так очевидно, а я тупой, то зачем, например, в Пайтоне и ПХП добавили аннотации типов? Чтобы усложнить чтение и поддержание кода? Решили элитностью пахнуть?

“Многие паттерны функционального программирования невозможны или крайне затруднены со статическими типами … композиции и функции высшего порядка”

  • Тоже нет. Вполне себе это типизируется. Другое дело, что в Рамде, на основании типизации которой автор делает такие заявления, используются хинты для Тайпскрипта 2.9. С тех пор качество вывода типов сильно улучшилось и вполне возможно, что соверменный Тайпскрипт 3.6 с этим справится, но только вот, похоже, что это никому не интересно - в тайпхинтах последний комит от лета 2018 года…

22:05 “Функциональное программирование, типы, Яваскрипт. Нужно выбрать любые два”

  • Ооо! Холиварненько! В каком-то случае Тайпскрипт или люди, писавшие тапхинты для Рамды не справились с выводом типа, поэтому мы выбросим Тайпскрипт вообще, а будем писать без типов, потому, что типы нам не подходят. Не попытаемся это сделать сами, не разберёмся, в чём проблема, даже не будем заводить репорт на гитхабе, нет-нет! Просто удалим Тайпскрипт, объявим типы бесполезными и будем писать по старинке.

23:00 “Получается почти все преимущества статических типов для анализа кода…“

  • Ну конечно! А как же рассказанное на 08:00? Что линтеры справляются хуже, чем строгий статический контроль типов?

23:20 “Написание тестов имеет … пользу… Код ревью…

  • Ну, как бы да, не поспоришь. Но как это связано с типизацией?

24:10 “Бороться с динамической природой яваскрипта нет смысла”

  • Ээээ, а кто-то собирался бороться? Типизация - она не про борьбу, а про дополнительный контроль. Ключевое слово “дополнительный”.

24:50 “… предлагаю использовать тайпскрипт, как тулзу для тайп-чекинга Яваскрипта”

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

25:50 “То есть одной строчкой вы сделали джаваскрипт динамическим языком с сильной типизацией … язык стал как Руби или как Питон”

  • Вот щас отлично! Динамическая сильная типизация. Запуском статического анализатора кода. Молодец, чо.

26:30 “Легко можем исправить, добавив опциональные типы … d.ts нотации … внешние файлы с аннотацией типов”

  • А вот теперь я вообще в некотором охреневании - только что топили, что типизация - это фу, шум, сложность и вот это вот всё. Поэтому мы не будем использовать типизацию… Но нет! Всё-таки будем, но чтобы не было всё так просто и шума было побольше, мы будем поддерживать два исходника - один в .js, другой в .d.ts, и следить за их синхронностью. Видимо, типизация в отдельном файле почему-то дешевле, чем “дорогая типизация” в декларациях. Жаль только, что нет никаких метрик и оценок на этот счёт.

28:40 “Это большой плюс … это заставляет не типизировать всё подряд, а писать определения только там, где это действительно необходимо”

  • Кажется, я понял, зачем был нужен этот доклад. У чуваков есть множество легаси-кода, который как-то хочется типизировать, но руковоство не даёт притащить Тайпскрипт и переписать на типах, поэтому мы сделаем доклад, что это не нам запретили, это мы сами не очень хотели и подведём под решение, на которое согласились все - оставляем легаси, но начинаем покрывать их типами в соседних файлах. К такой формулировке у меня вопросов нет, более того, это официально рекомендуемый процесс миграции на Тайпскрипт, изложенный на сайте.

30:35 “… линтер … код ревью … тесты … непрерывная интеграция … и только при условии, что вы всё это сделали, добавьте Тайпскрипт в качестве тулзы. Рассматривайте его как ес-линт для типов … добавить капельку d.ts … таким образом получаете 99% процентов профита от типизации при минимуме затрат”

  • Вот как это всё между собой связано, я не понял. И почему типизация должна быть последним пунктом - тоже.

Конец доклада.

Бонус:

31:20 - реплика от коллеги докладчика: пример про вебпак, в который добавили аннотации, запустили Тайпскрипт в режиме статической проверки и “на этом этапе они нашли кучу ошибок”. Что как бы полностью противоречит сказанным на 11:20 словам о том, что строгая типизация не помогает в уменьшении количества ошибок.


В итоге ни разу не убедили, что строгая типизация - это зло и его нужно избегать. А если не избегать, то использовать крайне осторожно.

Другое дело, если есть куча унаследованного кода на Яваскрипте, то мгновенно, понятное дело, не переключишься. И тут есть два варианта - или добавить аннотации в d.ts, или потихоньку новый функционал внедрять на Тайпскрипте, он отлично с Яваскриптом интегрируется. Мы, например, вполне себе успешно мигрируем с чистого Яваскрипта на Тайпскрипт, количество аннотаций удаётся поддерживать в очень ограниченных объёмах, а новые модули пишутся сразу на Тайпскрипте и встраиваются в существующий старый код, который обязательно когда-нибудь тоже портируем.