Monday, November 8, 2010
An import trick too useful to pass up
How *do* you tell good code from bad anyway?
Friday, September 10, 2010
Untitled
A few months ago, there was a useful tutorial on using CouchDB with Haskell. You can find the original here.
One weakness of these DB layers is that you have to verify your data, and the APIs to the input data are usually not typesafe. As it turns out, it's incredibly easy to use type-level programming to make your DB calls typesafe.
Lets take an idealized version of a typical DB get call:
getDBUnsafe :: (JSON a) => DB -> String -> IO a
getDBUnsafe = undefined
There's two weak spots here. The first is the part where we pass in the DB key, and the second is when we use the value. The latter is more insidious than the former, because whatever it is you're using to parse your JSON, it's probably a pure function, so if you're coding in the usual expedient way, you'll have no idea why the parse is failing, when the real cause is that you're fetching from the wrong DB.
So here's the framework for a solution that uses FunctionalDependencies and MultiParamTypeClasses to impose a constraint on the type of the DB key and stored value, based on the type of the DB.
class (JSON v) => DBTy a k v | a -> k, a -> v where
getDBName :: a -> String
getKey :: a -> k -> String
getDB :: (DBTy a k v) => a -> k -> IO v
getDB db k =
getDBUnsafe (getDBName db) (getKey db k)
So how we use this? Well you just define a dummy type for a DB like such:
type UserId = Int
data Avatar
= Avatar ByteString
deriving (Eq, Show, Ord, Typeable, Data)
instance JSON Avatar where
showJSON = toJSON
readJSON = fromJSON
data AvatarsDB
= AvatarsDB String
instance DBTy AvatarsDB Int Avatar where
getDBName (AvatarsDB name) = name
getKey _ = show
After that, you just replace your unsafe DB calls that look like this:
v <- getDBUnsafe "avatars" userId
with this:
v <- getDB AvatarsBB userId
Oh and here's the stuff you need to paste at the top of all this to get it to compile.
{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses, DeriveDataTypeable #-}
module FundepsExample whereimport Data.ByteString.Lazyimport Text.JSON
import Text.JSON.Generictype DB = String
I know it's a pretty silly example and use case, but I was surprised that there were folks in my local Haskell meetup who hadn't seen it, so I thought I should share it. It's saved me no end of errors ever since I put it to use.
Now in practice, I've found it more useful to create a KeyStringTy class instead of using that getKey bit. As it so happens, the sort of stuff I typically use to index my bit bucket database are also the sort of stuff that I use in RPC calls from the web. It's probably less correct, but it sure was dandier to code. Watch it bite me in the ass someday.
Tuesday, August 17, 2010
Monday, June 21, 2010
Mike Rowe Celebrates Dirty Jobs
Wednesday, May 19, 2010
Haskell & STM, Why no Applicative?
There was a fellow on #haskell the other day who was apprehensive about learning STM. Should he learn it after learning category theory? We assured him that Haskell's STM was fairly simple, whereas category theory is a dense liberal art that you study to enrich your mind. Someone pointed him to the wiki and he went on his way.
Afterwards, I perused the wiki (again). What struck me was that there was no example there that show you how to convert a plain old bunch of IO routines into STM routines. When I went to make one myself, I realized how brain-dead easy it is, but that there's something missing...
Anyway, here's a chalked up example where someone has to perform multiple time-consuming tasks (preferably in parallel), with the interesting routine in bold:
Wednesday, May 5, 2010
Academia isn't Broken. We Are.
http://brucejacob.tumblr.com/post/373498114/academia-and-the-decline-of-wealth-in-america
If academia is contributing to the lack of innovation in this country, then maybe it's because we expect the wrong things from academia? I don't mean to say this as another pompous American, but when I used to chat with friends from abroad back in school, I was struck by how many of them had a uniform educational experience. This wasn't a blanket effect and there were more than enough exceptions to produce many of the most awesome researchers I've met. Back at home though, even the countries that had effective programs to retain their top talent suffered from a lack of innovation.
By contrast, there is no standard curriculum at the top 5 US universities for CS. But most of the kids coming out there are shills anyway. I went to one of the good schools, and many kids (and their parents) were concerned about whether what they were learning would "prepare them for the real world". That basically meant: did it teach you Java or consulting? You see what's happening? What our education system didn't do to them, their own expectations of college did, and sadly, they seem to have done it to themselves just as badly as the education system of those foreign countries did.
But education isn't about churning out stamped spoons, and that's why crap like that No Child Left Behind Act bothered me so much. Where we went wrong is that we began viewing education as something everyone is entitled to, for all the wrong reasons. Education is not factory farming. Steve Jobs took calligraphy because he thought it was interesting, and no one thought what the Google guys were doing was "practical". I'll wager that the Google guys did it because it seemed like nerdy awesomeness to transform web search into a giant linear algebra problem. Just as Academia is about exploring the boundaries of what we know, Education is about enriching one's thought process, and that's the leading source of innovation in our modern economy: good ideas from the fringe, implemented intelligently and autonomously.
The entitlement of success that seems to follow from attending college is what's broken. The expectation that you will get a cushy 9-5 job in return for that diploma is what's broken. It in essence is a laziness of the mind, an unwillingness to chart out one's own path, the very idea of which is quite unacademic.
Wednesday, April 28, 2010
Dirty Tricks
Here is a type I'm using to record stats.
data UserzFormationViews
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
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
Wednesday, April 21, 2010
A Problem I've Encountered with Types
data User
= SystemUser SystemProcessInfo
| AnonUser TimeLoggedIn
| RegisteredUser UserId
data UserId
= PaidUser (..)
| FreeUser (..)
The Problem:
data A
= A1 X
| A2 Y
| A3 Z
instance Foo A where
getFoo (A1 x) = getFoo x
getFoo (A2 x) = getFoo x
getFoo (A3 x) = getFoo x