Edward Kmett writes on his blog that using the Co newtype (from the kan-extensions package), it's possible to derive a Monad from any Comonad. I'd like to learn how to mechanically do this for any Cofree f a, as for some Cofree types I don't know a different way to get a monad instance in a fool-proof way. For example the data type
data Tree a = Leaf a | Branch a (Tree a) (Tree a)
is isomorphic to
type Tree' = Cofree (Compose Maybe V2) -- V2 from `linear` package
and I find that type interesting as it can't be modeled as a Free monad, and also isn't Representable. My understanding is that Co Tree is also isomorphic to Tree, and has a monad instance. Phil Freeman has used Co extensively in his comonadic UI libraries, so it must be useful. In "Declarative UIs are the Future — And the Future is Comonadic!", he mentions:
In fact, under certain conditions on f, the Co (Cofree f) monad is isomorphic to a free monad which is determined by f.
While I could play type tetris to write functions to go between the two types, I'm not confident that that would necessarily lead to the correct implementation.
Out of the many possible angles for approaching your question, let's begin by having a closer look at
Co Tree. Stripping the newtype wrappers andCoTmachinery leaves us with the following isomorphism:Now, what is a
forall r. Tree (a -> r) -> rfunction like? If we are to obtain anrvalue, of whose type we have no prior knowledge, out of aTree (a -> r), we need to have anavalue (to which we can apply ana -> rfunction) and to choose a position in the tree (from which we will get said function). This suggestsCo Tree a, rather than being isomorphic toTree a, amounts to a tree position and a value. To confirm that, we can try to work out an isomorphism.Tree positions can be expressed as a path from the root. For the sake of expediency, I'll use
[Bool], withFalseandTruestanding for climbing the left and right branches respectively:One potential complication is that, since the trees have variable size, indexing by tree positions is, in principle, not total. In this case, though, we can sneak out of trouble by returning whatever value is found in a leaf, even if there would be extra steps to take according to the supplied path:
Treeis, in some sense, almost representable. In particular,flip bringis surjective but not injective: it would be an adequateindexweren't it for the handling of leaves, which are treated as if they were infinitely repeating subtrees.bringallows us to have one direction of the isomorphism, in the manner outlined at the start of this answer:As for the other direction, we can recover path and value out of a
Co Tree aby feeding it specially crafted trees:Co Tree, then, is isomorphic toNode, which in turn is isomorphic to(,) [Bool]. This suggests the monad instance ofCo Treeshould be equivalent to writer on the[Bool]monoid, and indeed sendingreturnand(>>=)through the isomorphism confirms that.This understanding of
Co Treein terms of positions in the "almost representable"Treesquares nicely with how it goes for actually representable comonads. Ifwis a representable comonad, there is a monoidmsuch that:And therefore:
That is, if
wis a representable comonad,Co wis isomorphic to the writer monad that is its left adjoint, and which can be taken to represent a specific site in the representable structure. As far as I can tell, the uses ofCoin the Phil Freeman library you mention are in a similar spirit ("We can useCo wto explore the state space").With all this discussion of
Co, up to now I didn't get to consider a monad instance forTree. It turns outTreeis a monad, with an instance given by theCofreeisomorphism. The relevant instance isAlternative f => Monad (Cofree f). It is a pretty interesting instance, actually: inBranch a l r >>= f,landrare ignored unlessf ais a leaf, in which case they will be used as a fallback to provide the subtrees:That is quite different from the usual
concat-like monad instances for list-like and tree-like types. As a final illustration, we can see how theMonadinstance forCofreegives rise to a distinct non-empty list comonad: