Oct 7, 2009

Multiple Inheritance in Scala vs. C++

I've made an interesting discovery today on how Scala's type system is slightly more awesome than C++'s type system. It has to do with traits.

There's a C++ project I'm working on where I'm pulling out some shared functionality from a few classes and putting it into another class that will be used like a mixin (one reason to learn a language with mixins - you get better ideas when you go back to languages that don't have them). It's fairly simple: I have a base class Creature, and I have a number of classes which inherit from Creature to define some specific behaviour. Two examples are Giraffe and Tiger. These two subclasses have very different habits when it comes to how they find their food, however there's some basic functionality involved in the mechanics of eating and starvation and stuff that is common. So I put that basic functionality into a class called Hungry:
class Hungry{
...
public:
void act();
};
In the act() method, it makes the creature slightly more hungry and if it is too hungry, it makes the creature die. Unfortunately, the Hungry class has no idea what die() is. So I could have Hungry inherit from Creature as well, however that will cause diamond problems when we start getting into many different traits for different creatures. The next step was to try putting die() as a pure virtual method in Hungry. Unfortunately that didn't work unless I explicitly defined die() in both Giraffe and Tiger, which would be annoying since die() is specified in Creature and works just fine. With that, the code ends up like this:
class Hungry{
...
public:
...
void act() {
if (--hunger <= 0)
die();
}

virtual void die() = 0;
...
};

class Giraffe : public Creature, public Hungry{
...
void die() { Creature::die(); }
...
};
This is really a minor detail, but slightly annoying. Shouldn't it be able to figure out that there is one concrete implementation of die() in Giraffe and Tiger?

Turns out that in Scala, it does figure this out:
class Creature{
def die(){
...
}
}

trait Hungry{
def act() = {
hunger -= 1
if (hunger <= 0)
die
}

def die // abstract method

var hunger : Int = 0
}

class Giraffe extends Creature with Hungry{
override def act() = {
super[Hungry].act
}
}
This works perfectly. It is type-safe too, if you remove the "extends Creature" from Giraffe, it will give you an error saying that you haven't defined die.

I'm confused, why can't C++ do this? This doesn't really have anything to do with the fact that Scala is using a trait, because if you made Hungry into a class and allowed Scala to inherit from multiple classes, it wouldn't be any different. The fact of the matter is that Giraffe and Tiger do have a die() method, but the C++ compiler isn't recognizing this and saying that they are abstract classes.

No comments: