Thank you!
I'll take this opportunity to expand on my note a bit. There are indeed several comonads in ATS generalizing the one I described. For example, one can construct:
vtypedef C(a: t0p) = [i: nat | i < n] [l: agz] (array_v(a, l, n), mfree_gc_v(l) | ptr(l))
Regard that as parametrizing arrays of type a (of fixed size n) with some choice of distinguished index i. There is an "extract" operation C(a) -> a that takes out
the i:th entry, and an "extend" operation
(C(a) -> b) -> (C(a) -> C(b))
that works as follows: An f: C(a) -> b reduces an array with a distinguished index to a term of b. Consider each index in turn as distinguished, to get n terms of b.
Then put those n elements into an array of size n of type b. Take the actual distinguished index as distinguished index of your new array. I haven't worked out the details
but it seems you can play similar games with most container types. What is sort of peculiar to me is that the "type part" in all cases is "ptr(l)", so almost all of the
comonadic abstractions are built on the view side of things, and the cost at runtime should be virtually nonexistent. I think it is certainly possible to do low-level programming
this way.
Comonads like the one described above are very useful. Consider image processing for example. Let the array be considered as a matrix, giving parameter values to the
pixels of a 2D image. There is often a recipe for transforming the parameters that depends on a pixel and its immediate neighbors, i.e. a function C(a) -> a. (A concrete
example is maybe a "blur" transformation, that takes a suitable average of the parameters of a pixel and that of its neighbors.) To transform the entire picture one would
"extend" to C(a) -> C(a). Conway's "Game of Life" is another example: Whether or not a cell survives to the next round depends on how crowded its immediate surrounding is.
Yet another example is parsing: You can't decide what to do with a single char without knowing its neighbors.
For a single transformation there is not so much gain in looking at things comonadically, but once you start composing several transformations of this form it can quickly get out
of hand without Kleisli composition to the rescue: since the effects depends on neighborhoods you can't change a point without changing how its neighbors will transform!
Programming in an imperative style with side effects side-steps the "need" for monads, but not for comonads. :)
My next ATS project will be to program an arduino-based effects pedal for electrical guitar. (I've seen people do that using C and there are many source code examples floating
around on the web.) Signal processing is another example of where comonads can be applied. (Say you measure a signal at discreet steps, but transform it based on neighboring
steps, so you have a contextual dependency.) I'm gonna give it a go!
PS. I think that in ATS it is best two imagine the types as forming not one category but two (or maybe three): type and viewtype. (Or t0ype and vt0ype.) The array construction
described above is perhaps most naturally regarded as a functor C: type -> viewtype. There is a sort of trivial inclusion J: type -> viewtype given by wrapping in a dataviewtype (so
datavtype J(a: type) = J of a) and one can regard C as a comonad relative to J.