Подтвердить что ты не робот

Теоретически, это действительный экземпляр comonad для списка?

Я пытаюсь понять концепцию comonads, и после прочтения этого сообщения в блоге, я думаю, что у меня есть четкое понимание того, что они делают и как они связанные с монадами. Но я подумал, что немного углубился бы в тему и подумал бы о том, как будет выглядеть экземпляр comonad типа универсального списка (вы знаете, [a]), и я пришел к тому, что я не полностью известно, что это правильно.

Итак, учитывая пример использования блога:

class Functor w => Comonad w where
    (=>>)    :: w a -> (w a -> b) -> w b
    coreturn :: w a -> a
    cojoin     :: w a -> w (w a)

Я думал, что объявление экземпляра для [a] будет выглядеть примерно так (синтаксис для [a], вероятно, невозможен или неверен, но вы получаете эту идею здесь):

instance Comonad [a] where
    coreturn = head
    cojoin = Data.List.subsequences --this is what I'm confused about
    x =>> f = map f (cojoin x)

Здесь мы просто находим все subsequences списка, но вполне возможно просто использовать его powerset или что-то еще. В списках формы (a -> [a]) есть несколько функций, и это несколько неоднозначно относительно того, какой из них правильный.

Означает ли это, что [a] не может быть правильно правильно создан как comonad, или просто для пользователя решить, что действительно будет делать cojoin?

4b9b3361

Ответ 1

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

Кроме того, ваш пример также должен удовлетворять законам comonad. Выраженные в терминах coreturn и cojoin, это:

  • coreturn . cojoin = id
  • fmap coreturn . cojoin = id
  • cojoin . cojoin = fmap cojoin . cojoin

Вы можете легко убедиться, что это не относится к вашему экземпляру, даже если мы запретили пустые списки. Однако, предполагая, что coreturn head, мы можем использовать эти законы, чтобы получить некоторые подсказки относительно того, что должно быть cojoin.

Из (1) мы можем определить, что первый элемент списка, возвращаемый cojoin, должен быть исходным списком, а из (2) мы видим, что объединение первых элементов каждого внутреннего списка также должно давать исходный один. Это говорит о том, что нам нужно что-то вроде tails * и можно подтвердить, что это также удовлетворяет (3).

* В частности, нам нужна версия tails, которая не включает пустой список в конце.

Ответ 2

Чтобы уточнить, что упомянули другие, рассмотрите следующий тип для непустых списков:

data NonEmptyList a = One a | Many a (NonEmptyList a)

map :: (a -> b) -> NonEmptyList a -> NonEmptyList b
map f (One x) = One (f x)
map f (Many x xs) = Many (f x) (map f xs)

(++) :: NonEmptyList a -> NonEmptyList a -> NonEmptyList a
One x     ++ ys = Many x ys
Many x xs ++ ys = Many x (xs ++ ys)

tails :: NonEmptyList a -> NonEmptyList (NonEmptyList a)
tails [email protected](One _) = One l
tails [email protected](Many _ xs) = Many l (tails xs)

Вы можете написать действительный экземпляр comonad следующим образом:

instance Functor NonEmptyList where
  fmap = map

instance Comonad NonEmptyList where
  coreturn (One x) = x
  coreturn (Many x xs) = x

  cojoin = tails

  -- this should be a default implementation
  x =>> f = fmap f (cojoin x)

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

Закон 1.

(coreturn . cojoin) xs = id xs
-- definition of `.`, `cojoin`, and `id`
(coreturn (tails xs) = xs
-- case on xs
  -- assume xs is (One x)
  (coreturn (tails (One x))) = One x
  -- definition of tails
  (coreturn (One (One x))) = One x
  -- definition of coreturn
  One x = One x

  -- assume xs is (Many y ys)
  (coreturn (tails (Many y ys))) = Many y ys
  -- definition of tails
  (coreturn (Many (Many y ys) (tails ys)) = Many y ys
  -- definition of coreturn
  Many y ys = Many y ys

  -- assume xs is _|_
  (coreturn (tails _|_)) = _|_
  -- tails pattern matches on its argument
  (coreturn _|_) = _|_
  -- coreturn pattern matches on its argument
  _|_ = _|_

Закон 2.

(fmap coreturn . cojoin) xs = id xs
-- definition of `.`, `cojoin`, `fmap`, and `id`
map coreturn (tails xs) = xs
-- case on xs
  -- assume xs is (One x)
  map coreturn (tails (One x)) = One x
  -- defn of tails
  map coreturn (One (One x)) = One x
  -- defn of map
  One (coreturn (One x)) = One x
  -- defn of coreturn 
  One x = One x

  -- assume xs is (Many y ys)
  map coreturn (tails (Many y ys)) = Many y ys
  -- defn of tails
  map coreturn (Many (Many y ys) (tails ys)) = Many y ys
  -- defn of map
  Many (coreturn (Many y ys)) (map coreturn (tails ys)) = Many y ys
  -- defn of coreturn
  Many y (map coreturn (tail ys)) = Many y ys
  -- eliminate matching portions
  map coreturn (tail ys) = ys
  -- wave hands.
  -- If the list is not self-referential,
  -- then this can be alleviated by an inductive hypothesis.
  -- If not, then you can probably prove it anyways.

  -- assume xs = _|_
  map coreturn (tails _|_) = _|_
  -- tails matches on its argument
  map coreturn _|_ = _|_
  -- map matches on its second argument
  _|_ = _|_

Закон 3.

(cojoin . cojoin) xs = (fmap cojoin . cojoin) xs
-- defn of `.`, `fmap`, and `cojoin`
tails (tails xs) = map tails (tails xs)
-- case on xs
  -- assume xs = One x
  tails (tails (One x)) = map tails (tails (One x))
  -- defn of tails, both sides
  tails (One (One x)) = map tails (One (One x))
  -- defn of map
  tails (One (One x)) = One (tails (One x))
  -- defn of tails, both sides
  One (One (One x)) = One (One (One x))

  -- assume xs = Many y ys
  (this gets ugly. left as exercise to reader)

  -- assume xs = _|_
  tails (tails _|_) = map tails (tails _|_)
  -- tails matches on its argument
  tails _|_ = map tails _|_
  -- tails matches on its argument, map matches on its second argument
  _|_ = _|_