четверг, 18 августа 2016 г.

Про м*

Нет, не про мудаков.

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

Так вот...

Это наиболее простое объяснение, которое я когда-либо встречал:

https://www.quora.com/Can-I-understand-monad-as-a-container-performing-certain-computation/answer/Bartosz-Milewski?share=a7312bea

В молодости ты все думаешь, что контейнер - это некая структура данных. Так вот... забейте, это не так. Думайте, что это контейнер (никакой STL тут не причем).

Самое важное, что есть в функциональном программировании (и вообще в программировании. и мире в целом) - это композиция  ("композибилити"). Это когда выход одного может быть входом другого. Для ФП - это как бы базис. Хваленное ООП не такое, тут понятие вход/выход выходит за рамки ООП. Ну а в мире, один из самых простых примеров - вилка-розетка. Это только потом Lego появилось.

Так вот... Иногда удобно, когда у вас выход чем-то обернут, назовем это "чем-то" контейнером. И хочется сохранить композицию. То есть следующая функция берет оригинал, и возвращает опять обертку. И мы это композируем.

В мире java до сих пор (2016 год у меня на дворе) любят null. Все-таки немного про мудаков. Всякие вариации nullable, Option[_] и есть контейнер. Мудаки все равно будут утверждать что null быстрее работает, что-нибудь про боксинг говорить. Мудаки как есть. Закончили про них.

Еще контейнер - список. Функция возвращает не просто один "детерминированный" вариант, а целый список. Список пустой - функция загнулась. Список из одного элемента, единственный результат. Несколько элементов - резветвление "недетерминированных" вычислений (тут кавычки нужны, оно конечно все детерминированно).

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

Аналогия с прямолинейным Option/nullable тут на лицо. Если - косяк, то - косяк. Иначе живем. Можно обернуть этот косяк к красивым сообщение об ошибке. Так вот... у нас есть еще один контейнер.

А фьючи..? Например поэтому не хочется говорить структура данных, это именно контейнер.

Про упоротых хаскеллистов я пока не буду, мне рановато. Все равно IO() немного маджик, там как-то скрыта ссылка на внешний мир.

State он прикольный, но я тоже ничего здесь писать не буду. У меня местами есть подозрение, что в какие-то моменты времени до меня доходят Free M* и trampoline. Трансформеры пока мимо кассы.

А так реальный пример с широкими массами: заметил у Сереги на работе код на swift-e. Тот унылый треугольник кода, где пять вложенных if-ов, в каждом проверка на null и что-то деланье. Выразил свое негодование. Через неделю проверил, там 4-5 вынесенных мини-функций, но все равно по факту лапша. Континуэшенами не втирал, но близко. Оказалось, что в swift у Option и так есть map/flatMap. Переписали в 4 строки, ни одного if-a.

В первые полгода сначала кажется, какой же классный синтаксис, for работает и со списками, и с фьючами, и с Option. Так вот, это не синтаксис, это и есть м*.

ЗЫ. Вот и философский вопрос. Предположим есть язык, в котором достаточно богатая типизация, м* вынесены как абстракция, но нет for-comprehension. Вопрос, на который день он появится?

1 комментарий:

Urbanowicz комментирует...

Главная ошибка объяснятелей монад в том, что они начинают рассказывать, что такое монада. Правильный подход — показывать, как они позволяют упростить код в разных случаях, например: http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf