Без вступлений и отступлений.
Последний раз ныряю.
Монады - это всего лишь способ композиции функций.
Предположим, у нас есть функции, "входы-выходы" которых совпадают по типам аргументов и возвращаемых значений.
Например, три какие-то функции - F1, F2 и F3.
И мы с помощью некоторой операции композиции функций формируем из них новую функцию, возвращающую результат последовательного применения функций (пусть странный символ "==" обозначает "эквивалентно"):
Fn(x) == F1( F2( F3( x )))
Тут всё прозрачно, понятно и тривиально. Если, конечно, совпадают следующие типы:
А если эти типы не совпадают, что тогда ?
Очевидно, нужен некий дополнительный код, даже не приводящий типы, а именно обеспечивающий работоспособность всей конструкции в целом.
Почему в этом случае нельзя говорить о приведении типов достаточно очевидно - размерности наличествующих и требуемых типов могут не совпадать.
Например, функция F3 может принимать скалярное значение с плавающей точкой (float), а возвращать вектор из целых чисел.
А F2, например, способна обрабатывать только одно целое число.
Естественно, способ "соединить входы-выходы" этих двух функций есть.
И достаточно очевидный (псевдокод, более императивный, чем функциональный):
foreach i in F3( x ) F2( i )
То есть, "к каждому элементу возвращённого F3 вектора применить функцию F2".
Само собой, результатом такой композиции F3 и F2 будет уже не скаляр, а вектор.
Аналогично, если и F1 способна принимать только одно целое число, приём можно "продлить" и на неё:
foreach j in (foreach i in F3( x) F2( i )) F1( j )
Всё очевидно, но программист возмущённо кричит "многабукав" и "ниасилил" и требует чтобы было как-нибудь как можно проще и универсальней.
Например, просто так:
Fn(x) = compouse F1 F2 F3
Естественно, ничего идеального не бывает, и далеко не все отличающиеся параметрами и возвращаемыми значениями функции можно "связывать" в подобные композиции.
А только функции со вполне определёнными сигнатурами (аргументами, их порядком и типами, а также типом возвращаемого значения).
Пригодная к "монадированию" (ох и термины, ох и термины) функция обязана:
В предыдущем примере базовым типом был тип скалярного объекта x - float, а monadic-объектом - возвращаемый функцией F3 вектор, "наполненный" целыми числами.
Для того, чтобы иметь возможность удобно применять пригодные к монадировнию функции в композициях, стало быть, надо всего лишь научиться изящно применять такие функции не к принимаемым ими базовым типам, а к monadic-объектам.
Изящно - без "лишнего" кодирования.
Для этого конструкция монад предусматривает функцию связывания, которая принимает в качестве параметров monadic-объект и монадируемую функцию, и внутри себя уже делает ту самую очевидную вещь, которую выше делал псевдокод foreach ...
Эту самую функцию "связывания" часто обозначают с использованием слова "bind", потому как она именно обеспечивает принципиальную возможность связывания "выходов" и "входов" монадируемых функций.
Выглядит она как-то так примерно (в разных языках - по-разному, но суть не меняется совершенно):
monadic_bind( monadic_object, monadic_function)
и внутри делает примерно это самое:
foreach i in monadic_object monadic_function( i )
Само собой, функция monadic_bind обязательно должна быть в каждой монадной конструкции.
Иными словами, то, что обходится без monadic_bind - то не монада и гоните её в шею, да.
А теперь унифицируем конструкцию.
В том смысле, что исключим вообще непосредственную передачу даже параметра базового типа монадируемой функции, и потребуем всегда использовать для вызова монадируемых функций только monadic_bind.
Ну вот хочется нам так, чтобы всё было как в армии - пусть безобразно, зато однообразно.
Для этого, очевидно, нужен преобразователь объектов базовых типов в monadic-объекты, потому как monadic_bind никаких таких базовых типов принимать не умеет - не положено ей.
Этот преобразователь - ещё одна важная деталь монады.
И тоже, что характерно, функция, принимающая объект базового типа и возвращающая monadic-объект, контейнер, на дне которого валяется этот самый один несчастный объектик.
Назовём эту функцию, например, monadic_result.
Итого.
Монада - это комбинация из:
Всё.
Теперь можно очень просто строить любые композиции из любых функций, имеющих указанную сигнатуру.
Собственно, вот и вся суть монад, будь они неладны.
Вот и вся тайна.