Easy in OOP–harder in FP: a list of animals

WARNING: what I describe here seems to be an antipattern
See Haskell Antipattern: Existential Typeclass

So better only use it with what is said in the Haskell-Wiki and mind the good comments bellow – Thank you Stephen and Jacob

Sometimes things you take for granted in your common OOP language (says C#) seems to be hard or impossible in Haskell at first glance.

This is no big deal – indeed the problem is so simple I thought for a moment I must have lost my mind over night as I could find no easy way to solve this in Haskell at first – and indeed the solution is quite simple but it involves a language extension I had not seen before (yeah – I’m still in my Haskell-noob-years – ask me again another decade or so Smiley mit geöffnetem Mund) but maybe this article can help some fellow beginner.

In this post I’m going to describe a possibility to access a list of animals (data) of different types that share some common traits like the number of limbs.

To see what I want look at the following short C# code:

interface IAnimal
    int NumberOfLimbs { get; }

class Dog : IAnimal
    public string Name { get; set; }
    public string Color { get; set; }

    public int NumberOfLimbs
        get { return 4; }

class Spider : IAnimal
    public int NumberOfLimbs
        get { return 8; }

static class Animals
    public static void DescribeAnimals()
        var animals = new IAnimal[] {new Dog {Name = "Mozart"}, new Spider()};
        foreach(var animal in animals)
            Console.WriteLine("This one has {0} limbs", animal.NumberOfLimbs);

The purpose of this is just to get a array/list of animals – you don’t even have to use a interface – a base class would serve the same purpose.

Easy right?

How hard can this be in Haskell?

I came upon this problem while trying to write a simple ray-tracer where a scene consists of a collection of renderable objects. Of course this is easy if you go and use an algebraic data-type for this. But I wanted the raytracer to be open (you want to provide some object other then spheres and polygons – go on: add them) – but of course ADTs are closed – so no luck there.

Of course the next thing you are thinking of are data and type-classes and indeed you can solve the problem in this way but it’s not as easy as it might seem.

Let’s get back to the example above – an open way to implement the NumberOfLimbs property could look like this:

module Animals where

class IBody a where
	nrLimbs :: a -> Int

newtype Dog = Dog String
instance IBody Dog where
	nrLimbs (Dog _) = 4

newtype Spider = Spider ()
instance IBody Spider where
	nrLimbs (Spider ()) = 8

But how can we get a list of things with different data-types (Dog and Spider) ? – Well we don’t – a list is a homogenous data-structure and IBody is not a data-type but a type-constructor so we have to say what ‘a’ is.

So we have to get a data-type that somehow can a instance of the IBody class without having to say what the data-type really is. Well you cannot do this in vanilla Haskell but we are not out of luck: there is an language-extension named “ExistentialQuantification” that can help us here: it gives you a “forall” quantifier just like in math that allows you to remove a concrete data type from your data-definition.

You can read a much better explanation than I could give here (HaskellWiki: Existential ypes) but here is what this can look like in our simple example:

data Animal = forall a. (IBody a) => Animal a

describeAnimals :: [Animal] -> [String]
describeAnimals []     = []
describeAnimals (a:as) = describe a : describeAnimals as
	where describe (Animal a) = "This one has " ++ (show $ nrLimbs a) ++ " limbs"

As you can see I added an Animal data-type whose constructor can wrap every IBody-Instance, but you don’t have to constraint it to any concrete type a because it’s “forall” those.

Of course now the compiler cannot know anything other of such an Animal besides that it’s of instance IBody – but that is exactly what we want.

The describeAnimals function finally takes advantage of this and can handle list of Animals, and you could use it like this:

> let animals = [Animal (Dog "Mozart"), Animal (Spider ())]
> describeAnimals animals
["This one has 4 limbs","This one has 8 limbs"]

Of course this is not the hole story and I encourage you to read the very good wiki article I linked above.