Saturday, June 18, 2011

Curiously Recurring Template Pattern

I was inspired to write this after seeing this question & answer on stackoverflow.com.
Several things occurred to me while reading it. I want to focus on the technical issue, but I can't help but mention a couple ancillary things that got me here. Before I start, I want people to keep in mind that this post is not meant to be a criticism of anyone. It is a criticism of certain ideas.
It bothers me that the "accepted answer" for the posted C# question (the question was: "Is there a more elegant way to do this?") on stack overflow basically boiled down to: "Try Haskell".
Really? That's not an acceptable answer. Not just because I disagree with it. But because it didn't actually answer the question. And do you really think the original poster--who marked that as the accepted answer--is now using Haskell to solve his problem? I doubt it.
Accompanying the answer was a link to a blog post, providing further details as to why people should avoid using this pattern.
It's an interesting read. And it's a valid opinion. And I couldn't disagree more.
Here's the problem, as envisioned by Eric Lippert:
public abstract class Animal<T> where T : Animal<T>
{
   public abstract void MakeFriends(T animal);
}

public class Cat : Animal<Cat>
{
   public override void MakeFriends(Cat cat) { }
}
The problem with this, according to Eric, is that we can still create a class that would violate our intentions, such as this:
public class EvilDog : Animal<Cat>
{
   public override void MakeFriends(Cat cat) { }
}
Now an EvilDog can make friends with a Cat!
You see, some people mistakenly think that when we defined the class Animal<T> with the constraint that T be a subclass of Animal<T>, that T must be the subclass itself, not another sister class underneath Animal<T>.
But this isn't a flaw in the pattern!
It's no different than this:
public class Cat
{
   public void MakeFriends(Cat cat) { }
}

public class EvilDog : Cat
{
}
Oh no! Even without using generics and the curiously recurring template pattern we can create an EvilDog that can make friends with a Cat!
Anyway...
I use the pattern quite regularly. It can be an enormously helpful tool. Like anything, it can be misused.
If it's imperative that T be the defined subclass, and it's seen as likely that developers won't realize that, you can mitigate that problem by doing something as simple as:
protected Animal()
{
   if (!(this is T))
      throw new ApplicationException("Can't do that!");
}
Problem (if there ever was one) solved.