Given any container type we can form the (element-focused) Zipper and know that this structure is a Comonad. This was recently explored in wonderful detail in another Stack Overflow question for the following type:
data Bin a = Branch (Bin a) a (Bin a) | Leaf a deriving Functor
with the following zipper
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 ...
It is the case that Zip
is a Comonad
though the construction of its instance is a little hairy. That said, Zip
can be completely mechanically derived from Tree
and (I believe) any type derived this way is automatically a Comonad
, so I feel it ought to be the case that we can construct these types and their comonads generically and automatically.
One method for achieving generality for zipper construction is the use the following class and type family
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
which has (more or less) shown up in Haskell Cafe threads and on Conal Elliott's blog. This class can be instantiated for the various core algebraic types and thus provides a general framework for talking about the derivatives of ADTs.
So, ultimately, my question is whether or not we can write
instance Diff t => Comonad (Zipper t) where ...
which could be used to subsume the specific Comonad instance described above:
instance Diff Bin where
data D Bin a = DBin { context :: [Step a], descend :: Maybe (Bin a, Bin a) }
...
Unfortunately, I've had no luck writing such an instance. Is the inTo
/outOf
signature sufficient? Is there something else needed to constrain the types? Is this instance even possible?