Изучай Haskell во имя добра! (Липовача) - страница 267

принимает монадическое значение в монадическом значении и отдаёт нам просто монадическое значение; другими словами, она его разглаживает. Вот она с некоторыми значениями типа >Maybe:

>ghci> join (Just (Just 9))

>Just 9

>ghci> join (Just Nothing)

>Nothing

>ghci> join Nothing

>Nothing

В первой строке – успешное вычисление как результат успешного вычисления, поэтому они оба просто соединены в одно большое успешное вычисление. Во второй строке значение >Nothing представлено как результат значения >Just. Всякий раз, когда мы раньше имели дело со значениями >Maybe и хотели объединить несколько этих значений – будь то с использованием операций ><*> или >>>= – все они должны были быть значениями конструктора >Just, чтобы результатом стало значение >Just. Если на пути возникала хоть одна неудача, то и результатом являлась неудача; нечто аналогичное происходит и здесь. В третьей строке мы пытаемся разгладить то, что возникло вследствие неудачи, поэтому результат – также неудача.

Разглаживание списков осуществляется довольно интуитивно:

>ghci> join [[1,2,3],[4,5,6]]

>[1,2,3,4,5,6]

Как вы можете видеть, функция >join для списков – это просто >concat. Чтобы разгладить значение монады >Writer, результат которого сам является значением монады >Writer, нам нужно объединить моноидное значение с помощью функции >mappend:

>ghci> runWriter $ join (Writer (Writer (1, "aaa"), "bbb"))

>(1,"bbbaaa")

Внешнее моноидное значение >"bbb" идёт первым, затем к нему конкатенируется строка >"aaa". На интуитивном уровне, когда вы хотите проверить результат значения типа >Writer, сначала вам нужно записать его моноидное значение в журнал, и только потом вы можете посмотреть, что находится внутри него.

Разглаживание значений монады >Either очень похоже на разглаживание значений монады >Maybe:

>ghci> join (Right (Right 9)) :: Either String Int

>Right 9

>ghci> join (Right (Left "ошибка")) :: Either String Int

>Left "ошибка"

>ghci> join (Left "ошибка") :: Either String Int

>Left "ошибка"

Если применить функцию >join к вычислению с состоянием, результат которого является вычислением с состоянием, то результатом будет вычисление с состоянием, которое сначала выполняет внешнее вычисление с состоянием, а затем результирующее. Взгляните, как это работает:

>ghci> runState (join (state $ \s –> (push 10, 1:2:s))) [0,0,0]

>((),[10,1,2,0,0,0])

Здесь анонимная функция принимает состояние, помещает >2 и >1 в стек и представляет >push 10 как свой результат. Поэтому когда всё это разглаживается с помощью функции >join, а затем выполняется, всё это выражение сначала помещает значения