Итак, в этой публикации вы не найдете ответы на следующие вопросы:
1. Что такое монада?
2. Где и как использовать монады?
3. Почему монады лучше, чем их отсутствие?
В программировании есть такой феномен — «паттерны проектирования». Официально это набор лучших практик, которыми следует руководствоваться при решении «типичных задач». Неофициально — просто набор костылей для языков, в которых нет встроенных средств для решения типичных проблем.
Есть такой паттерн проектирования — Interpreter. Замечателен он в первую очередь тем, что позволяет сделать некое подобие виртуальной машины поверх любимого языка программирования, при этом:
1. Можно описать программу на языке, понятном виртуальной машине.
2. Можно
Всё что написано ниже имеет смысл только если любезный читатель минимально знаком с упомянутым паттерном.
Сравнительно каноничный пример:
Любители GoF могут поспорить, мол, «это Command, а не Interpreter». Для них пусть это будет Command. В контексте статьи это не очень важно.
В этом примере, во-первых, есть программа, состоящая из двух инструкций: «добавить 10» и «разделить на 3». Что бы это ни значило. Во-вторых, есть исполнитель, который делает что-то
Договоримся, что трансляция add() в console.log() нам неинтересна. Интересны вычисления. Поэтому немного упростим код, отказавшись от ненужной гибкости:
Здесь стоит остановиться. У нас есть некий инструмент, позволяющий отдельно описывать программу и отдельно «способ её исполнения». В зависимости от наших пожеланий к результату исполнения, реализация исполнителя может быть очень разной.
Например, хочется, чтобы как только где-то в вычислениях появляется NaN,null или undefined, вычисления прекращались и возвращался результат null:
Хорошо. А что если мы хотим одну и ту же программу выполнять для коллекции разных начальных значений? Тоже не вопрос:
Здесь снова стоит остановиться. Мы используем одни и те же выражения, чтобы описывать программу, но в зависимости от исполнителя получаем очень разные результаты. Попробуем теперь снова немного переписать пример. В этот раз, во-первых, уберём ещё немного гибкости: выражения теперь выполняются строго от первых — к последним, а во-вторых, избавимся от цикла внутри run(). Результат назовём словом Context (чтобы никто не догадался):
Реализация сильно отличается от предыдущих вариантов, но делает оно примерно то же самое. Здесь предлагается ввести термин мунада (от англ.moonad — «лунная реклама»). Здравствуй, Identity moonad:
Эта штука чем-то отдалённо похожа на Identity monad.
Вспомним теперь про тот вариант исполнителя, где мы боролись с NaN и попробуем переписать его используя новый подход к реализации:
Можно даже более привычный пример:
Издалека может показаться, что это Maybe monad. Любезному читателю предлагается самостоятельно реализовать что-то похожее на List monad.
При базовых навыках работы напильником не составит изменитьIdentityMoonad таким образом, чтобы вызовы f() стали асинхронными. В результате получится Promise moonad (что-то похожее на q).
Теперь, если внимательно приглядеться к последним примерам, можно попробовать дать более-менее формальное определение мунады. Мунада — это штука, у которой есть 2 операции:
1. return — принимает обычное значение, помещает его в мунадический контекст и возвращает этот самый контекст. Это просто вызов конструктора.
2. bind — принимает функцию от обычного значения, возвращающую обычное значение, выполняет её в контексте мунадического контекста и возвращает монадический контекст. Это вызов `bbind()`
Комментариев нет:
Отправить комментарий