In my application, I'm trying to implement an animation system. In this system, animations are represented as a cyclic list of frames:
data CyclicList a = CL a [a]
We can (inefficiently) advance the animation as follows:
advance :: CyclicList a -> CyclicList a
advance (CL x []) = CL x []
advance (CL x (z:zs)) = CL z (zs ++ [x])
Now, I'm pretty sure that this data type is a comonad:
instance Functor CyclicList where
  fmap f (CL x xs) = CL (f x) (map f xs)
cyclicFromList :: [a] -> CyclicList a
cyclicFromList [] = error "Cyclic list must have one element!"
cyclicFromList (x:xs) = CL x xs
cyclicLength :: CyclicList a -> Int
cyclicLength (CL _ xs) = length xs + 1
listCycles :: CyclicList a -> [CyclicList a]
listCycles cl = let
  helper 0 _ = []
  helper n cl' = cl' : (helper (n-1) $ advance cl')
 in helper (cyclicLength cl) cl
instance Comonad CyclicList where
  extract (CL x _) = x
  duplicate = cyclicFromList . listCycles
The question I have is: what kind of benefits do I get (if any) from using the comonad instance?
 
                        
The advantage of providing a type class or implementing an interface is that code, written to use that typeclass or interface, can use your code without any modifications.
What programs can be written in terms of
Comonad? AComonadprovides a way to both inspect the value at the current location (without observing its neighbors) usingextractand a way to observe the neighborhood of every location withduplicateorextend. Without any additional functions, this isn't terribly useful. However, if we also require other functions along with theComonadinstance, we can write programs that depend on both local data and data from elsewhere. For example, if we require functions that allow us to change location, such as youradvance, we can write programs that depend only on the local structure of the data, not on the data structure itself.For a concrete example, consider a cellular automata program written in terms of
Comonadand the followingBidirectionalclass:The program could use this, together with
Comonad, toextractdata stored in a cell and explore the cellsforwardandbackwardof the current cell. It can useduplicateto capture the neighborhood of each cell andfmapto inspect that neighborhood. This combination offmap f . duplicateisextract f.Here is such a program.
rule'is only interesting to the example; it implements cellular automata rules on neighborhood with just the left and right values.ruleextracts data from the neighborhood, given the class, and runs the rule on each neighborhood.slicepulls out even larger neighborhoods so that we can display them easily.simulateruns the simulation, displaying these larger neighborhoods for each generation.This program might have been intended to work with the following
BidirectionalComonad, aZipperon a list.But will also work with a
CyclicListBidirectionalComonad.We can reuse
simulatewith either data structure. TheCyclicListhas a more interesting output, because, instead of bumping into a wall, it wraps back around to interact with itself.