Чтобы понять результат, вам нужно понять экземпляр Comonad[NonEmptyList]
. Comonad[W]
по существу предоставляет три функции (фактический интерфейс в Scalaz немного отличается, но это помогает с объяснением):
map: (A => B) => W[A] => W[B]
copure: W[A] => A
cojoin: W[A] => W[W[A]]
Итак, Comonad
предоставляет интерфейс для некоторого контейнера W
, который имеет выделенный элемент "head" (copure
) и способ раскрытия внутренней структуры контейнера, так что мы получаем один контейнер на элемент ( cojoin
), каждый с данным элементом в голове.
Способ, которым это реализовано для NonEmptyList
, заключается в том, что copure
возвращает начало списка, а cojoin
возвращает список списков, с этим списком в начале и всеми хвостами этого списка в конце .
Пример (я сокращаю NonEmptyList
до Nel
):
Nel(1,2,3).copure = 1
Nel(1,2,3).cojoin = Nel(Nel(1,2,3),Nel(2,3),Nel(3))
Функция =>=
состоит из композиции Кокейсли. Как бы вы сочинили две функции f: W[A] => B
и g: W[B] => C
, ничего не зная о них, кроме того, что W
является Comonad
? Тип ввода f
и тип вывода g
несовместимы. Тем не менее, вы можете map(f)
получить W[W[A]] => W[B]
, а затем составить это с g
. Теперь, получив W[A]
, вы можете cojoin
, чтобы получить W[W[A]]
для подачи в эту функцию. Итак, единственная разумная композиция - это функция k
, которая выполняет следующее:
k(x) = g(x.cojoin.map(f))
Итак, для вашего непустого списка:
g(Nel(1,2,3).cojoin.map(f))
= g(Nel(Nel(1,2,3),Nel(2,3),Nel(3)).map(f))
= g(Nel("Nel(1,2,3)X","Nel(2,3)X","Nel(3)X"))
= BigInt(Nel("Nel(1,2,3)X","Nel(2,3)X","Nel(3)X").map(_.length).sum)
= BigInt(Nel(11,9,7).sum)
= 27