Композируем монады

Хорошо известно, что monads do not compose. Почему?

Здесь существует известная путаница, когда на практике под композицией ошибочно понимают организацию цепочек вычислений (с помощью bind монады). Bind лишь вытаскивает значение из монады и передаёт его в функцию, которая выдаёт на выходе монаду. Композицией в математическом смысле здесь и не пахнет.

В то же время можно успешно композировать и функторы, и аппликативные функторы. Вот заметка на эту тему:
/training/o27_exercises_in_programming_style_11/

Но почему же монады нельзя? Вроде бы и в формальной композиции функций тоже используется некое подобие цепочек вычислений?

Парадоксально оказывается, что именно из-за bind мы и не можем, буквально, «складывать» монады вместе — в отличие от функторов. Потому что, как только у нас появляется возможность склеивать монады в вычисления, мы не можем гарантировать, что получим корректный процесс. Условно говоря, если мы будем использовать монаду-парсер для Питона и монаду-парсер для Хаскеля, состыковать их вместе невозможно. Или, например, если мы выполняем переделку AST, то монада, формирующая AST из Python, и монада, формирующая AST из Haskell, возможно и скомпозируются, однако совсем не факт, что результат в контексте одного из этих языков будет семантически корректен.

В общем случае, если есть две системы (монады), каждой из которых присуще свойство X, при их композиции не гарантируется, что и резульату также будет присуще свойство X. Если монада А работает без специфических ошибок и монада Б работает без специфических ошибок, то не факт, как уже говорилось, что их композиция будет безошибочна.

Математики, наверное, сказали бы, что в результате композиции монад не получается моноид. Формально же, при композиции монад результом просто не будет монада. А при композиции функторов, в том числе аппликативных, результатом, наоборот, будет корректный функтор.

Ещё одна интерпретация проблемы: fmap формирует новую категорию, хоть и с исходной структурой, и если в таком случае функторы можно корректно применять по всем таким категориям (структура которых одинакова), то с монадами такой подход не работает, так как не гарантируется, что монада сохраняет исходную структуру.

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

На эту тему есть большое исследование 1993-го года «Composing monads» (Mark P. Jones, Luc Duponcheel), где рассматривается случай корректной композиции с некоторыми ограничениями.

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

Поделиться статьей ...Share on Facebook0Share on Google+0Tweet about this on TwitterShare on LinkedIn0Share on VKPrint this page

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *