Надёжность ФП-кода и типизация

Функциональному коду тесты якобы не нужны — цепочка вычислений либо собирается, либо падает, ошибочный результат не возвращает. TypeScript — не единственный путь к надёжности; до него был Google Closure Compiler с JSDoc. Имя функции — простейшая форма типа.

Вопрос не про язык, а про организацию

Фактически всё то, что касается надёжности и скорости разработки, лежит вне плоскости Vanilla.js vs TypeScript. Это вопрос об организации собственной разработки.

Намёк: TypeScript якобы даёт ответ, но не даёт. TS — компилятор, который не справляется с произвольным API на уровне семантики.

TypeScript тоже не спасёт

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

TS стреляет в ногу при Object.assign, сложной generics-логике, dynamic dispatch. Найдутся кейсы, где TS бесполезен.

Google Closure Compiler

До TypeScript существовали инструменты, которые намного более эффективно решали те же задачи. Google Closure Compiler решал бы те же задачи, выводя типы при помощи JSDoc.

/**
 * @param {number} x
 * @returns {number}
 */
function double(x) { return x * 2; }

JSDoc + Closure Compiler ≈ TypeScript для тех, кто не хочет билд-чейн.

Имя функции как тип

Сам факт объявления функции с именем theObject — это уже самое простое определение типа. Это мы ещё не погрузились в теорию категорий. В процессе вызова мы можем проконтролировать тот самый параметр.

const theObject = (data) => (fn) => fn(data);
// theObject(123) НЕ даёт тебе функцию-получатель
// theObject({name:'X'}) даёт корректную цепочку

В функциональной парадигме тип задаёт сама сигнатура функции. Имя — простейший тип.

Функциональному коду тесты не нужны

Именно поэтому для функционального кода никто тестов не пишет. Невозможно переопределить поведение, чтобы нарушить функцию doThing — потому что она принимает theObject.

Тезис спорный, но логика такая:

  • Цепочка либо собирается, либо нет
  • При несовпадении контрактов код падает (а не возвращает мусор)
  • Если функция чистая и контракт сошёлся — она работает

Контроль типов в рантайме через Proxy

const doThing = new Proxy(target, {
  get(t, key) { /* контроль доступа */ },
  apply(t, ctx, args) { /* контроль вызова */ }
});

Можно организовать работу через Proxy, контролировать доступ к property и сам вызов. Это императивный путь к тому же результату.

Сначала система, потом бизнес-логика

Смысл работы программиста: он должен сначала придумать архитектуру, создать необходимое количество функций, которые обеспечивают работу системы, и потом программировать бизнес-логику.

Boilerplate возникает там, где не выстроена архитектура.

ООП всегда порождает химер

ООП всегда порождает химер. Вы создаёте животное о четырёх ногах, потом наследуете животное на двух задних лапах, потом без рук, и приходится мутировать объект.

ООП упрощает мир до базовых свойств + наследования. Цепочка наследований выстраивает противоестественную иерархию.

Дислексия и выбор ФП

Я с дислексией. Могу удержать в голове в лучшем случае 3 объекта. ООП казалось бы прямым путём, но не зашло. Функциональная форма оказалась понятной и ясной.

ФП эффективнее отображает жизнь, но кажется сложнее, потому что не вписывается в иерархическую модель мышления.

ООП взлетело из-за персоналок

ФП всё это время развивалась и была доминирующей парадигмой. Всё изменилось, когда персоналки шагнули в наш мир. Рынок потребовал большого количества программистов, но не был готов ждать 5-6 лет.

ФП-программисты — годы обучения. ООП/императив дают специалиста за полгода. Так появилась Java как эталон класс-ООП.

Правила ФП — следствие, не требования

Если вы пишете код в функциональном стиле — это не требования, это следствие. Эти правила проистекают из того, как вы пишете код. Вы не заставляете себя следовать правилам.

Чистота, иммутабельность — не цели, а свойства, которые получаются сами.

Связанные темы

Ресурсы