Comonad duplicate function

Почему, когда вы определяете функцию duplicate

   duplicate :: w a -> w (w a)     

для comonad typeclass (ссылка) вам нужно изменить все элементы "в контексте" (т.е. изменить элементы, отличные от текущее значение контекста). Почему бы просто не использовать что-то вроде return в Monad?

Пример (молния):

   data Z a = Z [a] a [a]     

почему я не могу просто определить дубликат как

   duplicate z = Z [] z []     

Я попытался получить требование для функции duplicate из правил Comonad, но я всегда получаю дубликат, который просто обертывает элемент, похожий на return в монады без необходимости делать что-либо еще.

Один сообщение в блоге говорит:

дублировать немного сложнее. Из заставки на молнии нам нужно создать список молний из списка молний. Значением этого (подтвержденным законами comonad, которые должен выполнять каждый экземпляр) является то, что перемещение внутри дублированной структуры возвращает исходную структуру, измененную тем же движением

Но я не понимаю, почему так должно быть. fmap в правилах Comonad всегда применяется к обернутому элементу, а затем этот один элемент всегда "разворачивается" с помощью extract, зачем беспокоиться о чем-либо еще в дублирующем элементе , кроме как просто перенос аргумента duplicate?

Можете ли вы указать, что я пропустил? Я чувствую, что где-то я сделал какую-то очевидную ошибку, но я просто не могу понять это сам.

Заранее благодарим за ответы!

Ответы

Ответ 1

Важно, если вы можете делать другие вещи с типом, а не просто extract от него. Интуитивно, если единственное, что вы можете сделать, это извлечь значение, тогда тип содержит только одно значение, поэтому дублирование этого значения дублирует все. Это вообще не так, и это не так для молний.

Законы Comonad - это просто законы категории, маскирующиеся на функции типа w a -> b. Поскольку они исходят из категорий, было бы легче рассуждать о них по категории, а не по законам Comonad. extract является личностью этой категории, а =<= является оператором композиции.

-- | Right-to-left 'Cokleisli' composition
(=<=) :: Comonad w => (w b -> c) -> (w a -> b) -> w a -> c
f =<= g = f . extend g

Мы также знаем, что extend f = fmap f . duplicate, поэтому мы можем написать

f =<= g = f . fmap g . duplicate

Это выглядит довольно легко рассуждать. Теперь позвольте оборудовать ваш тип Z другой функцией, о которой мы можем поговорить. isFirst вернет true только тогда, когда Z представляет значение в позиции в списке, в котором ничего нет.

isFirst :: Z a -> Bool
isFirst (Z [] _ _) = True
isFirst  _         = False

Теперь рассмотрим, что происходит, когда мы используем isFirst с тремя законами категории. Кажется, что единственное, что кажется ему применимым, - это то, что extract является левым и правым тождеством для композиции по =<=. Поскольку мы только опровергаем это, нам нужно найти встречный пример. Я подозреваю, что один из extract =<= isFirst или isFirst =<= extract не будет введен для ввода Z [1] 2 []. Оба они должны быть такими же, как isFirst $ Z [1] 2 [], который равен False. Сначала мы попробуем extract =<= isFirst, что будет работать.

extract  =<=   isFirst               $  Z [1] 2 []
extract . fmap isFirst . duplicate   $  Z [1] 2 []
extract . fmap isFirst $ Z []          (Z [1] 2 [])  []
extract                $ Z [] (isFirst (Z [1] 2 [])) []
extract                $ Z []  False                 []
                               False

Когда мы попробуем isFirst =<= extract, нам не повезет.

isFirst  =<=   extract               $  Z [1] 2 []
isFirst . fmap extract . duplicate   $  Z [1] 2 []
isFirst . fmap extract $ Z []          (Z [1] 2 [])  []
isFirst                $ Z [] (extract (Z [1] 2 [])) []
isFirst                $ Z []  2                     []
True

Когда мы duplicate d, мы потеряли информацию о структуре. Фактически, мы потеряли информацию обо всем, что получилось везде, кроме единственного фокуса на молнии. Правильный duplicate имел бы всю "застежку-молнию" всюду в контексте, содержащую значение в этом местоположении и этот контекст местоположения.

Посмотрим, что мы можем вывести из этих законов. С небольшой рукой, размахивающей о категории функций, мы можем видеть, что =<= extract есть fmap extract . duplicate, и это должна быть функция тождества. По-видимому, я заново открываю, как законы написаны в документации для Control.Category. Это позволяет нам написать что-то вроде

z = (=<= extract)              z
z = fmap extract . duplicate $ z

Теперь Z имеет только один конструктор, поэтому мы можем заменить это на

Z left x right = fmap extract . duplicate $ Z left x right

Из типа дубликата мы знаем, что он должен возвращать тот же самый конструктор.

Z left x right = fmap extract $ Z lefts (Z l x' r) rights

Если применить fmap к этому Z, мы имеем

Z left x right = Z (fmap extract lefts) (extract (Z l x' r)) (fmap extract rights)

Если мы разделим его на части конструктора Z, мы получим три уравнения:

left = fmap extract lefts
x = extract (Z l x' r)
right = fmap extract rights

Это говорит нам о том, что, по крайней мере, результат duplicate (Z left x right) должен выполняться:

  • список с той же длиной, что и left для левой стороны
  • a Z с x посередине для среднего
  • список с той же длиной, что и right для правой стороны

Кроме того, мы можем видеть, что средние значения в списках слева и справа должны быть такими же, как и исходные значения в этих списках. Учитывая только этот закон, мы достаточно знаем, чтобы потребовать другую структуру для результата duplicate.