Sunday, April 27, 2014
'W' is for It's My BIRTHDAY today! ... and Comonads
'W' is for 'Whaaaaat...?' (Comonads) ... and 'W' is for 'It's my birthday today! YAY!'
That's actually a 'Y' in 'YAY,' but when you string two Y's together: YY, you get something that looks like a W ... or two martini glasses next to each other, husband and wife, take your pick.
And 'Wife' starts with the letter 'W,' so there you go!
Wife, n: ORIGIN Old English wīf [woman,] of Germanic origin; related to Dutch wijf and German Weib.
All very ... Vikingescque.
... In a good way.
So, 'W' is for 'It's my party and I'll cry if I want to, cry if I want to, cry if I want to! You would cry, too, if this happened to you. Da-da-da-da-da-dat!' (youtube vid)
But I don't feel like crying at all! I (ain't) happy, I'm feeling glad, I've got sunshine, in a bag, I'm useless, but not for long, my future is coming on! [Gorillaz cartoon clip]
So, yeah, that.
Okay, so to the (mathematical) topic. Why do I say 'W' is for comonad? And what is a comonad, anyway, and what is the meaning of life, the universe, and everything?
That last one is a piece of cake:
I'm so glad Deep Thought covered that ground for me. Otherwise I would have looked like I didn't have the answer to something, and would've had to have made something up to assuage the masses!
But anyway, why are we even having a blog-post day for the letter 'W,' when, after all, it's as plain as day that there's no such thing as the letter 'W'! I mean, 'double'-'u' it's two 'v's together, and even 'v' is a constructed thing from 'u,' (in the Greek there is no 'v,' they use 'β' so it's Blad, the impaler), so 'v' and 'w,' today and yesterday, are silly posts for silly days for letters that don't even exist!
AND it's my birthday today, and did I get cake? Huh? Did I? Huh? Or am I here, all alone, writing this blog entry about the non-extant letter 'W' and comonads? Huh? Huh? I ask you!
(Cue entry of my daughter, EM, baring gifts of gold, frankincense and cake.)
Ooh! Cake! What flavor is that?
EM: Dark chocolate mousse.
Ooh! My favorite!
Excuse me a moment whilst I indulge after much being-sung-Happy-Birthday-to.
Okay, I'm back.
Okay, geophf, how in the world does 'W' have anything to do with Comonads?
Okay, so Comonads are the dual to monads, and, in the literature, the monadic types are represented by 'm' and monadic functions are prefixed with a little-m or suffixed by a capital-M (depending on their mode of use:
msum :: Monad m, Num a => [m a] -> m a
liftM :: Monad m => (a -> r) -> m a -> m r
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
You see how that works? m-functions work with monads, simplifying them, whereas function-Ms work with non-monad forms, putting them into the monadic domain), complexifying simpler objects.
So that explains the 'M' for monads ...
... although there is the whole question lingering as to why they are even called 'monads' (one thing) as they are the triple:
(M, μ, η) where:
M is the monadic type,
μ is the join-function such that join :: Monad m => m (m a) -> m a; and,
η is the unit-function such that unit :: Monad m => a -> m a
(the unit-function, in Haskell, is known as the return-function)
Monads from mathematics were originally called 'triples,' because that's what they are, monads from philosophy mean something else entirely, so occasionally we get a logician in the mathematics forum asking what the hell we're all taking about!
So, 'monads' (misnamed) are represented by 'm' for their type-families.
So, comonads (mis-co-named) are monads' duals, so their type-families are represented by the inverted symbol of monads, comonads have the type-variable: w.
Get it? Got it? Good!
Now, I could go all 'w' is 'ω' and say that it represents Ohm, and Ohm's laws, ...
but then this post would be all chanting:
Ohm, Ohm, Ohm, Ohm, Ohm, Ohm, Ohm, ... (youtube link)
And there's something in that for some, I suppose, but not here nor now.
Here and now we're talking about the comonad.
The Comonad is the dual of the monad, so it is a co-triple of the form:
(W, δ, ε) where:
W is the Comonadic type
δ is the duplicate-function such that duplicate :: Comonad w => w a -> w (w a); and,
ε is the extract-function such that extract :: Comonad w => w a -> a
For monads, you can only push 'plain-old' objects up into the monadic domain and once there, you never leave it. You can't get an object out of a monads.
For comonads, you can only extract 'plain-old' objects from the comonadic context, and once extracted, it's free of the comonad. You can't make comonads from plain-old objects.
For monads, they have join and monadic-bind, stringing together operations in the monadic domain:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
m >>= f = join . fmap f
(given that the monad is a functor, as well)
For comonads, they have duplicate and comonadic-extention, extending the comonadic context over the computation:
(=>>) :: Comonad w => w a -> (w a -> b) -> w b
w =>> f = fmap f . duplicate
(again, when the comonad is a functor as well)
So, like, Monads and comonads are, so, like, totes the opposite.
Dude. (The Dude Abides)
For me, personally, I didn't 'get' comonads for a long, long time. What was the point of them? I wondered, but now I see them everywhere.
The object-model of object orientation is not so well-understood as the categorical representation of functional programming, but one approach is the Ω-calculus.
That's one way of seeing OOP. I see it, perhaps in the same way, but, I don't think so. I see object-orientation with inheritance from the Art of the MetaObject Protocol, and from that perspective, objects and inheritance are totally comonadic. You have the computational context of the parent class and you extend that to your child class (or your child class extends the computational context).
For-loops, modeled by folds, yes, but these are also an entirely comonadic thing. You're extending the context of your loop over the life of your computation.
Comonads are everywhere!
... except not maybe so much in if-then-else ... monads fit that so much more naturally, or a transformation on the Either type.
Comonads. Don't worry about it if you heard about them but you just don't get them. I was there for years. Just keep doing what you're doing, 'normal' functional programming, and you're way ahead of the game. Monads and applicative functors? Go to town! Arrows? You rock.
But, for me, one day it came to the point that I was monad-ing myself to death, and I wondered, 'is there a better way?'
And then that's when I looked at comonads, again.
So, where do I use them now? and for what? and how?
The 'how' is so trivial it's really pointless to go over them. Most people, for this very reason, look at comonads, and say, 'yeah, but ... that's the map-function, right?' and eh-onto other things.
Kind of like how monads should be used.
"Eh, but that's a function, right?" and eh-move onto your next task.
But monads are so novel for most people, that we're still in the honeymoon phase, and we will be there, for, oh, another fifty years or so (it will take that long for the very idea to be introduced and then absorbed into the mainstream programming culture).
But until that time, there'll be this love-affair with monads, with the dual-neglect of comonads (note that on the wikipedia page, comonads are a footnote to monads. This dismissive treatment of comonads is not uncommon). Sigh.
I use comonads to look at things in other ways.
Say I have this list-of-lists:
[2011-03-24,62.52,62.99,59.75,62.63,5398500,62.44], ... etc.
And instead of that raw data, I wish to observe some trends in it:
Date,Open,High,Low,Close,Volume,Adj Close,sma 15,ema 12, ema 26, accum_over_distr,adx 20
[2011-03-24,$62.52,$62.99,$59.75,$62.63,5398500,$62.44,62.63,61.73466415405274,61.73466415405274,4198833.333333336,100.0], ... etc
Comonads allow me to do this quite simply by using a little, simple technique called regression:
> regression :: ([Row] -> a) -> [Row] -> [a] -- your classic comonad
> regression f rows = rows =>> f
That takes a formula, in this case an indicator, such as the SMA (simple moving average) or the EMA (exponential, or weighted, moving average), and scanning the entire screen, giving the result for each row, returning the entire screen to you, again, but now enhanced by the comonadic function applied throughout the screen.
That's what a comonad is. You have this whole big thing, like a stock-screen of GMCR ('Green Mountain Coffee Reserves'), and the comonad takes the entire thing to operate on, and gives the result an unit (or a row) at a time.
Comonads are perfectly-fitted to the uses for which I intend them, now that I see them, finally, and know what they are and how to use them. I have this big old thing. I want to see it in a different way, and I know what result I want piece-by-piece, but, unlike for monads, that deal piecewise-at-a-time, comonad gives you the entire scope of the computational context, so if you need the previous row, for example, you have it and every other row in the history.
'W' is for Comonad. Don't worry if you don't 'get' them. They are there. They'll wait for you.
It's just that when you do start using them, you'll see their usefulness in a lot more places that you had heretofore neglected to use them, and you'll find, going forward, your work is cleaner and simpler because of them.