Vue v3 был обновлен, чтобы включить новый Composition API, который, как и обновление хуков React пару лет назад, полностью охватывает шаблоны функционального программирования.

И хуки, и Composition API являются отражением того факта, что функциональное программирование штурмом захватило мир разработчиков за последние несколько лет.

Около двух лет назад я начал более серьезно изучать функциональные концепции, потому что хотел наконец понять, что такое монада. Я до сих пор не до конца их понимаю, но я хорошо разобрался в функциональных принципах.

Каковы основные принципы работы?

Многие разработчики уже знают об этом, поэтому кратко обрисую их в общих чертах:

  1. Функции должны иметь возможность заменять буквальное значение.
  2. Они должны принимать только один аргумент.
  3. Они не должны изменять свои ценности.
  4. Они не должны полагаться или вызывать побочные эффекты (например, использование других функций из глобальной области видимости или добавление к window).

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

Переломный момент

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

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

Фаза 1

Хорошо, это самая тупая версия кода, она идет шаг за шагом. Сначала я беру элемент, значок твиттера, по его идентификатору. Затем я добавляю прослушиватель событий щелчка, который открывает новое окно со ссылкой на Twitter, encodeURI просто проверяет правильность форматирования пробелов и специальных символов.

Это отлично работает, но его нельзя использовать повторно, и он работает только для Twitter.

Фаза 2

Давайте завернем его в функцию, создадим аргументы id и url и вызовем функцию, передав специфические для Twitter части, а не запрограммировав их жестко.

Теперь это шаг к тому, чтобы быть функциональным — ну, по крайней мере, это функция! — но есть пара проблем. Во-первых, мне не нравится, что window.location.href используется внутри функции, это означает, что мы можем использовать эту функцию только для href текущего окна.

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

Фаза 3

Чтобы решить проблему onclick, мы будем использовать вместо этого addEventListener, таким образом, мы можем просто использовать ?. для доступа к нему, не опасаясь выдать какие-либо ошибки.

Еще я переместил window.location.href в верхнюю область видимости, так что теперь функция не так сильно мутирует url.

Это лучше, теперь эта функция даже не предназначена специально для обмена в социальных сетях, это просто помощник для добавления любого URL-адреса к событию клика любого элемента.

Но это все еще не функциональная функция. Он принимает более одного аргумента, использует document.getElementById, window.open и encodeURI, которые являются побочными эффектами, технически мутирует url с encodeURI и ничего не возвращает!

Фаза 4

Если мы собираемся сделать addUrlToOnclick правильно функционирующим, он не может полагаться на document.getElementById, мы должны сначала получить элемент, а затем передать его в качестве аргумента:

Затем мы хотим вернуть значение, чтобы это был элемент с добавленным к нему событием клика:

[Считается ли element.addEventListener мутацией? Донно, неважно.]

Кроме того, window и encodeURI также необходимо использовать вне функции, поэтому мы просто используем функцию обратного вызова в качестве второго аргумента.

Мы больше не можем называть это addUrlToOnclick, потому что мы удалили эту функцию. Назовем его addEventToElement.

Также у нас все еще слишком много аргументов, функциональное программирование допускает только один. Пусть addUrlToOnclick возвращает другую функцию, которая принимает второй аргумент:

Вот как это будет называться:

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

Пойдем дальше.

Фаза 5

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

Но это сломается, потому что windowOpen вызывается, как только вы передаете его в аргументе, поэтому давайте вернем третью функцию:

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

Теперь код — как и в начале — не очень пригоден для повторного использования, поэтому давайте просто используем encodeURI непосредственно внутри windowOpen

…и превратить elementById в функцию…

Теперь все является функцией:

Теперь, я не знаю о вас, но я думаю, что это зашло немного далеко. Если бы я хотел повторно использовать addEventToElement, мне пришлось бы также перетаскивать еще две функции.

В общем, я только что переписал addEventListener. Я был вынужден сделать свою функцию слишком низкоуровневой, чтобы выполнять ту работу, которую я хотел.

Кроме того, это выглядит очень запутанным… для меня. Я не думаю, что очень понятно, что происходит.

Какую фазу мне следует остановить?

Думаю, многие скажут, что в конце этапа 5

Здесь код больше всего похож на хук React. Понятно, что происходит, реализация довольно прозрачна и функциональна, но не претенциозна.

Но на самом деле я остановился на Фазе 4

Это была наиболее повторно используемая и полнофункциональная фаза сценария.

Мне пришлось использовать эту функцию для трех отдельных элементов, и хотя версия Phase 5 была наиболее прозрачной, эта сделала то, что мне было нужно, и заняла гораздо меньше места.

Но это совсем не функционально.

Является ли функциональное программирование претенциозным?

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

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