Монады в Haskell иногда называют «программируемыми точками с запятой». Это не фраза, которую я нахожу особенно полезной в целом, но она отражает то, как выражения, написанные с пометкой Haskell do
, имеют что-то вроде императивных программ. И, в частности, способ объединения «операторов» в блоке do
зависит от конкретной используемой монады. Следовательно, «программируемые точки с запятой» - способ объединения последовательных «операторов» (которые во многих императивных языках разделяются точками с запятой) может быть изменен («запрограммирован») с использованием другой монады.
И так как do
на самом деле нотация - это просто syntacti c sugar для построения выражения от других с использованием оператора >>=
, это реализация >>=
для каждой монады, которая определяет, каково ее «специальное поведение».
Для Например, экземпляр Monad
для Maybe
позволяет в качестве грубого описания работать со значениями Maybe
, как будто они на самом деле являются значениями базового типа, обеспечивая при этом отсутствие значения (т. е. Nothing
) происходит в любой точке, результатом является короткое замыкание вычислений, и Nothing
будет общим результатом.
Для монады списка каждая строка фактически «выполняется» несколько раз (или ни одной) - один раз для каждый элемент в списке.
А для значений монады State s
это, по сути, "функции манипулирования состоянием" t ype s -> (a, s)
- они принимают начальное состояние, и из этого вычисляют новое состояние, а также выходное значение некоторого типа a
. Реализация >>=
- «точка с запятой» - делает здесь * просто гарантирует, что, когда за одной функцией f :: s -> (a, s)
следует другая g :: s -> (b, s)
, результирующая функция применяет f
к исходному состоянию и затем применяет g
до состояния, вычисленного из f
. По сути, это просто композиция функций, немного измененная, чтобы также позволить нам получить доступ к «выходному значению», тип которого не обязательно связан с типом состояния. И это позволяет перечислять различные функции манипулирования состоянием одну за другой в блоке do
и знать, что состояние на каждой стадии точно такое, которое вычисляется предыдущими строками, сложенными вместе. Это, в свою очередь, допускает очень естественный стиль программирования, в котором вы даете последовательные «команды» для манипулирования состоянием, но в то же время без фактического выполнения деструктивных обновлений или иного ухода из мира чистых функций и неизменных данных.
* строго говоря это не >>=
, а >>
, операция, которая получается из >>=
, но игнорирует выходное значение. Вы, возможно, заметили, что в примере, который я дал a
, значение, выводимое f
, полностью игнорируется, но >>=
позволяет проверять это значение и определять, какие вычисления делать дальше. В нотации do
это означает запись a <- f
и последующее использование a
. Это на самом деле ключевая вещь, которая отличает монад от их менее могущественных, но все же жизненно важных кузенов (особенно аппликативных функторов).