2

A pointed container is a comonad, and this observation can be used to obtain some useful effects, like for example stencil convolutions, almost for free. Further it is noticed that an instance Comonad may be obtained in a standard way via the Store comonad (by the means of a partially applied index function), and it would work just as fine as a hand-written definition.

But this approach has a drawback: Store is essentially a function, and once something is made into a function, going back is uncomfortable. And I notice there are situations such that a small change in requirements requires one to completely abandon the notion of a comonad, and return to the consideration of the initial container. I find it similar to final tagless evaluation: local transformations are easy, but the more context they require, the more complicated it gets. One may recover any portion of the initial container by iterating extraction over the corresponding subset of the cursor space, but then why trade the initial type for the final in the first place?

In other words, there are 2 ways to represent a container, and both are equally conducive to the definition of instance Comonad:

  1. As an "initial" data type. Without Dependent Types, the trivial definition of instance Comonad in Haskell, when a container is paired with an index, is partial (index may point to a place outside the shape of the container), but it will still work.
  2. As a "final" partially applied index function. A container may be made into a comonad by wrapping it in Store. It is then paired with an index automagically. Partiality argument applies equally, so there is no safety to be gained.

When the initial type is traded for the final function, all the methods defined for the initial type are lost until recovered with more or less elaboration. Given that access to a small finite container is unsafe (most indices are undefined), and that the container itself is not accessible to query, programming becomes a game of minesweeper. Whatever properties of the initial container I have not providently put in an EnvT, are lost. All in all, whatever safety or convenience benefit Store is supposed to give is contested by the hardships of handling a final representation of a container.

Put more concisely, a pointed container is, among other things, a comonad, but Store is only a comonad.

So, what is the motivation for Store? And can we get an instance Comonad, or at least Extend, more cheaply?

Ignat Insarov
  • 4,660
  • 18
  • 37
  • No one likes Store? – Ignat Insarov Nov 11 '19 at 05:25
  • I quite like `Store`! But I’m slightly confused about this question: to clarify, are you asking why anyone would use `data Store s a = Store (s -> a) a` instead of a concrete data type like a list or a map? – bradrn Nov 11 '19 at 23:02
  • @bradrn Yes! Or rather, why it was designed this way — what is the abstract justification for encoding a _container_ _finally_? _(Since any comonad is necessarily a container, it also admits an initial representation with data constructors.)_ – Ignat Insarov Nov 11 '19 at 23:13
  • Possibly, it could be because that allows you to represent any container in terms of its index type? `Store k v` is a pointed map, `Store Int a` is an infinite list, `Store Bool a` is a 2-tuple… in general, it seems very neat to me that you can represent any container using just one type. (Have a look at `Representable` if you want to learn more about this.) But then again, just because something is theoretically neat doesn’t mean that it’s practical or efficient! – bradrn Nov 11 '19 at 23:41

0 Answers0