Монады в Haskell иногда называют «программируемыми точками с запятой». В целом я не нахожу эту фразу особенно полезной, но она отражает тот факт, что выражения, написанные с помощью нотации Haskell do
, имеют что-то вроде императивных программ. И, в частности, то, как комбинируются «операторы» в блоке do
, зависит от конкретной используемой монады. Следовательно, «программируемые точки с запятой» - способ, которым последовательные «операторы» (которые во многих императивных языках разделяются точкой с запятой) объединяются вместе, можно изменить («запрограммировать») с помощью другой монады.
И поскольку нотация do
на самом деле является просто синтаксическим сахаром для построения выражения из других с помощью оператора >>=
, именно реализация >>=
для каждой монады определяет ее «особое поведение».
Например, экземпляр Monad
для Maybe
позволяет, в качестве грубого описания, работать со значениями Maybe
, как если бы они на самом деле были значениями базового типа, при этом гарантируя, что если незначение (то есть Nothing
) возникнет в любой точке , вычисления завершатся короткими замыканиями, и Nothing
будет общим результатом.
Для монады списка каждая строка фактически "выполняется" несколько раз (или ни разу) - один раз для каждого элемента в списке.
А для значений монады State s
это, по сути, "функции управления состоянием" типа s -> (a, s)
- они принимают начальное состояние и из него вычисляют новое состояние, а также выходное значение некоторого типа a
. Реализация >>=
— «точка с запятой» — здесь* просто гарантирует, что когда за одной функцией f :: s -> (a, s)
следует другая g :: s -> (b, s)
, результирующая функция применяет f
к начальному состоянию, а затем применяет g
к состоянию, вычисленному из f
. По сути, это просто композиция функций, слегка измененная, чтобы мы также могли получить доступ к «выходному значению», тип которого не обязательно связан с типом состояния. И это позволяет перечислить различные функции манипулирования состоянием одну за другой в блоке do
и знать, что состояние на каждом этапе точно такое же, как вычислено предыдущими строками вместе. Это, в свою очередь, позволяет использовать очень естественный стиль программирования, когда вы даете последовательные «команды» для управления состоянием, но при этом фактически не выполняете деструктивные обновления или иным образом не выходите из мира чистых функций и неизменяемых данных.
*строго говоря, это не >>=
, а >>
, операция, производная от >>=
, но игнорирующая выходное значение. Вы могли заметить, что в примере, который я дал, значение a
, выводимое f
, полностью игнорируется, но >>=
позволяет проверить это значение и определить, какое вычисление делать дальше. В нотации do
это означает запись a <- f
, а затем использование a
позже. На самом деле это ключевая вещь, которая отличает монады от их менее мощных, но все же жизненно важных кузенов (особенно аппликативных функторов).
person
Robin Zigmond
schedule
02.03.2020
>>=
монады State. Это гарантирует, что когда состояние изменяется одним выражением, обновленное значение передается следующему. - person Robin Zigmond   schedule 02.03.2020Stack
,push
иpop
? - person Mark Seemann   schedule 02.03.2020pop
). поэтому эквивалентный фрагментpush 3 >>= (\() -> pop >>= (\3 -> pop))
. (возможно, если так определяетсяpush
). в чем именно ваш вопрос? Haskell — это YAPL, вы, программист, отвечаете за него. - person Will Ness   schedule 02.03.2020>>=
не просто возвращает вторую вещь! - person user253751   schedule 02.03.2020State
состоит в том, чтобы скрыть явную передачу состояния (в данном случае манипуляции со стеком). - person chepner   schedule 02.03.2020push 3
. - person chepner   schedule 02.03.2020a
был связан по какой-то причине, но он никогда не используется. - person chepner   schedule 02.03.2020