Прежде чем я начну с этого, требуется некоторое введение.

Во-первых, я не профессиональный функциональный разработчик и, вероятно, никогда им не стану, поскольку такие работы очень редки. Мой фон - ООП, и изучение языка FP - это невероятный опыт. Радостная прогулка, которой я не чувствовал с тех пор, как был новичком. Если вам нравится пробовать что-то новое, я очень рекомендую это.

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

Почему эта статья?

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

Теперь, если вы откроете LinkedIn, вы обнаружите аналогичный гудок о том, как их продукт специально адаптирован к потребностям клиентов, бла-бла-бла. Дело в том, что я очень часто сталкиваюсь с такими словами. Мне также интересно, действительно ли они знают, что такое FP.

Вы используете функции высшего порядка? Нет, это не основная концепция ... Lambdas можно использовать и в C #, и в Java. Функция каррирования? Справедливо, но все же промах.

Основы FP

Как ни странно, FP не о функциях, а о типах. Да, вы правильно прочитали. Типы. Сами функции разработаны и написаны с учетом типа, а не наоборот. Черт возьми, даже функции рассматриваются как типы ... То же самое верно даже в JS / TS.

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

Одна из основных идей, когда дело доходит до функциональной архитектуры программы, заключается в том, что у вас есть Чистый модуль (или слой). Вот где происходит бизнес-логика. Он задокументирован предопределенными типами данных (я назову их чистыми типами), и все, что находится вне вашего контроля, удаляется из него. Проверка данных, операции ввода-вывода и т. Д.… Все это сдвинуто к краям этого модуля.

Взгляните на диаграмму выше. У нас есть часть Входные данные. Это может быть запрос клиента или чтение базы данных. Эти данные необходимо сопоставить с чем-то, что может понять наша бизнес-логика (чистый тип). Его также необходимо проверить перед входом в Pure Module, чтобы нам не приходилось иметь дело с неверными данными внутри него, а только с бизнес-логикой. Перед вводом неверные или поврежденные данные необходимо перехватить и удалить. Аналогичным образом, взаимодействие с внешними службами или что-либо, что находится вне нашего контроля (не бизнес-логика), также должно быть доведено до крайности. Это выходная часть. Запись в базу данных, отправка ответа пользователю, ведение журнала и т. Д.

Вызов БД может завершиться ошибкой из-за проблем с подключением. Запись в файл может завершиться ошибкой из-за неправильного пути к файлу. Бизнес-логика полностью находится под нашим контролем, и она никогда не должна давать сбоев. Он может одобрить или отклонить какое-либо действие, вернуть ответ или причины отклонения, но он никогда не может дать сбой или вызвать исключение.

Я вернусь к тому, как это связано с фронтом, ниже. А пока давайте посмотрим, как работает Pure Module…

Типы данных FP

Рассмотрим следующий интерфейс TS:

Чертовски просто… Однако сконцентрируйтесь на электронной почте. Все сообщения электронной почты являются строками, но не все строки являются сообщениями электронной почты. Они должны следовать определенному шаблону, например:

[email protected]

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

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

Как мы гарантируем эту действительность? Простой. У каждого типа будет свой собственный модуль функций, и его можно будет создать только с помощью этого модуля.

Может быть ‹T›

Прежде чем демонстрировать эти модули, мне нужно сначала поговорить о чем-нибудь другом ... еще раз ...

Помните, что типы Имя пользователя и Электронная почта сопоставляются с любой строкой? Это означает, что преобразование может завершиться неудачно. В Haskell, например, тип Maybe используется для того, чтобы скрыть нули. Ну, не совсем так, но просто согласитесь с этим для этого поста. Однако нам также нужен способ возврата ошибок преобразования, поэтому я буду использовать это имя повторно, но немного расширю функциональность этого типа.

Давайте разберемся с этим.

Отметьте первые два типа, которые я объявил:

Они оба являются объектами, и у них есть общее предопределенное свойство tag, которое может иметь только два значения: errors или success. Шутки в сторону. Компилятор заплачет, если вы попытаетесь назначить что-то другое. Это способ включить сопоставление с образцом. Не полноценное, исчерпывающее сопоставление с образцом, но кое-что для начала.

Теперь сосредоточьтесь на типе Может быть ‹T›:

Он может иметь значение типа Успех ‹T› или Ошибки, и, как упоминалось выше, мы используем тег для сопоставления с шаблоном. Может использоваться для результатов преобразования чистых типов. В случае успеха он возвращает объект, в противном случае он возвращает список ошибок для преобразования. Он является общим, поэтому один и тот же принцип можно использовать как для электронной почты, имени пользователя,, так и для любого другого типа, который вы придумаете.

Также существует модуль Maybe, который имеет функции успеха и ошибок. Это используется только для упрощения создания объектов Успех ‹T› и Ошибки.

Вернемся к типам Имя пользователя и Электронная почта и их соответствующим модулям:

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

Здорово. Вы поймете, зачем это нужно. Наконец, модуль Пользователь. У меня не получилось уместить это на снимке экрана, поэтому я пропущу строку за строкой. Сначала шаблон:

И построчная реализация. Во-первых, мне нужен объект-валидатор:

Вот почему я объявил этот небольшой тип полезности выше. Если вы начнете вводить это, компилятор будет плакать, пока вы не добавите проверку для каждого поля. Если кто-то изменит тип Пользователь, программа не будет компилироваться, пока он также не обновит эту часть. Итак, мы получили безопасность типов.

Затем проверьте, прошла ли проверка, если нет, сделайте ранний возврат со списком всех ошибок.

Если все выглядит радужно, розово и солнечно, создайте и верните Пользователя

Небольшое примечание: я не на 100% удовлетворен этим решением. Вам придется проделать все эти шаги для каждого проклятого типа. Я попытался создать дополнительный модуль валидатора, который бы содержал несколько общих функций, но TS был работает против меня по какой-то причине. В конце концов, это всего лишь полировка над гигантской какашкой, которой является JS. А какашку можно только отполировать до такой степени ... Если вы не смотрели эту серию "Разрушителей легенд" ...

Эээ ... Смысл всего этого?

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

Стоит ли оно того?

Лично я нахожусь в большой категории НЕЕЕТ ... Это привело бы к слишком большому количеству сопоставлений и добавило бы много сложности в код. Чистые типы также не допускают значений NULL. Итак, если что-то действительно является необязательным / допускающим значение NULL, вам придется упаковать его в тип Может быть ‹T› и соответственно работать с чистой частью. Весь поток был бы другим. Кроме того, единственной целью компонентов в любой библиотеке или фреймворке является рендеринг HTML, что является побочным эффектом, следовательно, не чистым ... Это усилие было бы попыткой летать на каменных крыльях. Лучше сделать функцию валидатора для обычного типа и усердно ею пользоваться.

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

За исключением того, что вы можете полностью со мной не согласиться. И я призываю вас поправить меня, если это необходимо. Основная идея этой статьи заключалась в демонстрации некоторых функциональных концепций, которые редко обсуждаются. Если вы просто используете map / filter / reduce… это не делает ваш код функциональным. В итоге, не стоит слепо цепляться за подножку и верить в каждую чушь, которую вы можете найти в сети (включая этот пост). Используйте свой разум и исследуйте себя.

Это ни в коем случае не треп. Я призываю вас попытаться подобрать правильный язык программирования FP, немного похожий на Scala, F #, Haskell и т. Д.… У них есть свое место, но дело не в пользовательском интерфейсе.

Спасибо за чтение