Wednesday, April 28, 2010

Dirty Tricks

Here is a type I'm using to record stats.

data UserzFormationViews
= UserzFormationViews
{ formationsViewed :: [(FormationId, UTCTime)]
, ...
}

Basically, the data is a monoid. Actually, it's even less restrictive than that, because the monoidal append on this piece of data is commutative (i.e. embarrassingly parallel). It's incredibly convenient to be able to just throw a pile of these together and tabulate the stats with a familiar mconcat.

At other times, it's useful to get all the user's views, in a form like this:

class RetrieveViews a where
getViews :: a -> [(UserId,FormationId,UTCTime)]

This obviously messes with the monoidal definition of UserzFormationViews. The initial object (i.e. mempty) would need a dummy value for the UserId. In more primitive languages, you'd just leave that as NULL, which is a dirty hack. And we don't like those kinds of dirty hacks in Haskell. What do we do instead?

instance RetrieveViews (UserId, UserzFormationViews) where
...

vs = getViews (zeUserzId,zeUserzViews)

So wrong and so awesome at the same time. :)

No comments: