Понимание того, почему Zipper является Comonad

Это ответ на ответ на мой предыдущий вопрос.

Предположим, мне нужно сопоставить каждый элемент a:A от List[A] до b:B с помощью функции def f(a:A, leftNeighbors:List[A]): B и сгенерировать List[B].

Очевидно, я не могу просто вызвать map в списке, но я могу использовать список zipper. Застежка-молния - это курсор для перемещения по списку. Он обеспечивает доступ к текущему элементу (focus) и его соседям.

Теперь я могу заменить my f на def f'(z:Zipper[A]):B = f(z.focus, z.left) и передать эту новую функцию f' в cobind метод Zipper[A].

cobind работает следующим образом: он вызывает f' с помощью молнии, затем перемещает застежку-молнию, вызывает f' с новой "перемещенной" молнией, снова перемещает застежку-молнию и т.д. и т.д... пока молния не дойдет до конца списка.

Наконец, cobind возвращает новую застежку-молнию типа Zipper[B], которая может быть преобразована в список, и поэтому проблема решена.

Теперь обратите внимание на симметрию между cobind[A](f:Zipper[A] => B):Zipper[B] и bind[A](f:A => List[B]):List[B]. Поэтому List является Monad и Zipper является Comonad.

Имеет ли смысл?

Ответ 1

Поскольку этот вопрос регулярно появляется в верхней части списка "без ответа", позвольте мне просто скопировать мой комментарий в качестве ответа здесь - ничего более конструктивного не появилось с года назад в любом случае.

A List также можно рассматривать как комонад (по-разному), а Zipper можно отличить как монаду (также по-разному). Разница заключается в том, концептуально ли вы концептуально сосредоточены на "добавлении" данных на конечный автомат (что означает интерфейс Monad) или "извлечение" из него "деконструктивно" (что делает Comonad),.

Нелегко ответить на вопрос, заявляя, что "понимает ли это понимание смысл". В некотором смысле это происходит, а в другом - нет.