Для любого типа контейнера мы можем сформировать (привязанный к элементам) Zipper и знать, что эта структура является Comonad. Это было недавно подробно изучено в еще одном вопросе для следующего типа:
data Bin a = Branch (Bin a) a (Bin a) | Leaf a deriving Functor
со следующей молнией
data Dir = L | R
data Step a = Step a Dir (Bin a) deriving Functor
data Zip a = Zip [Step a] (Bin a) deriving Functor
instance Comonad Zip where ...
В этом случае Zip
является Comonad
, хотя построение его экземпляра немного волосатое. Тем не менее, Zip
может быть полностью механически получен из Tree
и (я полагаю) любой тип, полученный таким образом, автоматически является Comonad
, поэтому я считаю, что это должно быть так, что мы можем построить эти типы и их comonads в общем и автоматическом режиме.
Одним из способов достижения общности конструкции молнии является использование следующего семейства классов и типов
data Zipper t a = Zipper { diff :: D t a, here :: a }
deriving instance Diff t => Functor (Zipper t)
class (Functor t, Functor (D t)) => Diff t where
data D t :: * -> *
inTo :: t a -> t (Zipper t a)
outOf :: Zipper t a -> t a
который имеет (более или менее) показанный в потоках Haskell Cafe и в блоге Conal Elliott. Этот класс может быть создан для различных основных алгебраических типов и, таким образом, обеспечивает общую структуру для обсуждения производных от ADT.
Итак, в конечном счете, мой вопрос заключается в том, можем ли мы писать
instance Diff t => Comonad (Zipper t) where ...
который может использоваться для включения конкретного экземпляра Comonad, описанного выше:
instance Diff Bin where
data D Bin a = DBin { context :: [Step a], descend :: Maybe (Bin a, Bin a) }
...
К сожалению, мне не повезло написать такой экземпляр. Достаточна ли подпись inTo
/outOf
? Есть ли что-то еще для ограничения типов? Возможно ли, что этот пример возможен?