Class and Instance

Today, we want a generic Animal class, so that we can instantiate animals out of it in a easy way. How can we do that?
First, we’ll want to create our generic class. We do it this way:

class Animal c where
doSomething :: c -> String
name :: c -> String

But we have a problem here. We can’t do

class Animal c where
doSomething :: String
name :: String

Because Haskell forces us to use the type that will be instantiated in our instances. We’ll cover on that later (*).
So let’s make our Cat and Dog instances now

data Dog = Dog
data Cat = Cat

instance Animal Dog where
doSomething _ = "Woof!"
-- name = "A dog" -- you can't do this
name _ = "A dog"

instance Animal Cat where
doSomething _ = "Meow!"
name _ = "A cat"

Now we can do some fancy stuff like

*Main> doSomething Dog
"Woof!"
*Main> doSomething Cat
"Meow!"
*Main> name Dog
"A dog"
*Main> name Cat
"A cat"
*Main>

So, basically, what we just wrote is “syntactic sugar” for

data DogOrCat = Dog' | Cat'

name' Dog' = "A dog"
name' Cat' = "A cat"

doSomething' Dog' = "Woof!"
doSomething' Cat' = "Meow!"

But the problem with this approach is that, whenever we want to instantiate another animal, we’d have to change the already existing structure (DogOrCat). We don’t want that.
We also risk in calling name’ for an Animal which may not have an instance. Instance forces you to instantiate all functions in the Class.
(*) Basically, this is what triggered this post: I had a discussion with a friend of mine today, and he said that you can’t do something like this in Haskell:

data Dog = Dog { name :: String; age :: Int, bark :: BarkKind }
data Cat = Cat { name :: String, age :: Int, meow :: MeowKind } -- error.

Which is correct. The reason for this is the namespace. name and age (declared in Dog) are global functions, and we are trying to re-declare them in Cat.
He wanted this kind of structure in order to be able to parse some JSON stuff.
I headed over to #haskell and had a discussion, where one guy had this implementation as a suggestion:

instance FromJSON Dog where
parseJSON (Object v) =
Dog v .: "name"
v .: "bites"
parseJSON _ = mzero

instance FromJSON Cat where
parseJSON (Object v) =
Cat v .: "name"
v .: "likes-humans"
parseJSON _ = mzero

This is using the Aeson library. Basically, that’s what Aeson did. They declared some generic class, so that we can make instances out of it in a elegant way where types will not conflict with each other.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s