This is a follow-up to the answer to my previous question.
Suppose I need to map each item a:A
of List[A]
to b:B
with function def f(a:A, leftNeighbors:List[A]): B
and generate List[B]
.
Obviously I cannot just call map
on the list but I can use the list zipper. The zipper is a cursor to move around a list. It provides access to the current element (focus
) and its neighbors.
Now I can replace my f
with def f'(z:Zipper[A]):B = f(z.focus, z.left)
and pass this new function f'
to cobind
method of Zipper[A]
.
The cobind
works like this: it calls that f'
with the zipper, then moves the zipper, callsf'
with the new "moved" zipper, moves the zipper again and so on, and so on ... until the zipper reaches the end of the list.
Finally, the cobind
returns a new zipper of type Zipper[B]
, which can be transformed to the list and so the problem is solved.
Now note the symmetry between cobind[A](f:Zipper[A] => B):Zipper[B]
and bind[A](f:A => List[B]):List[B]
That's why List
is a Monad
and Zipper
is a Comonad
.
Does it make sense ?