принимает монадическое значение в монадическом значении и отдаёт нам просто монадическое значение; другими словами, она его разглаживает. Вот она с некоторыми значениями типа
>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
, а затем выполняется, всё это выражение сначала помещает значения