April 2008 Archives

I'm learning Ruby as a sort of professional development effort and I like what I see so far. Ruby's a quick, concise and rich language that brings a lot to the table in terms of language constructs and platform support. Or so it would seem; I reserve the right to rescind this endorsement until I've spent a good month or so working with it in depth. In any case, I'm having fun with my first new language in a year or so.

One aspect that I find merits further thought is this notion of duck typing. What is duck typing you say? I'm glad you asked. It boils down to this oft-used analogy:

If it walks like a duck and quacks like a duck, I would call it a duck.

That is to say, if the object I'm working with behaves like an instance of a duck, it's a duck for all intents and purposes. Or, more bluntly, invoke whatever operation you want on an object, as long as it says it can do it. But what does this get us? It's not immediately obvious that it's all that quicker to code with these looser typing restrictions. Consider the following C++ function:

class Duck
{
    public:
        virtual string Quack()
        {
            return "Quack!";
        }
};

void QuackLikeADuck( const Duck& aDuck )
{
    std::cout << aDuck.Quack() << "!" << std::endl;
}

And now the same code in Ruby:

class Duck
    def Quack
        return "Quack!"
    end
end

def QuackLikeADuck( duck )
    if duck.respond_to?("Quack")
        puts duck.Quack();
    else
        raise "Called with an invalid object type"
    end
end

The Ruby version seems a bit more verbose, but this is a contrived example and I would concede that the sort of type checking in that function wouldn't always be necessary. If you're the only one using that code, you can make certain assumptions about those objects that will be passed into that function.

But where this sort of agility really shines is in it's dynamic nature. What if my application now demands the introduction of a Human type, but for some reason it needs to quack like a duck too? Consider the C++ approach:

class Human : public Duck
{
    public:
        string Quack()
        {
            return "Ceci n'est pas un duck!";
        }
};

A brittle class hierarchy to be sure. To be able to use the QuackLikeADuck as originally declared, I've been forced to have my Human subclass Duck. What if a previous class hierarchy had already been established and there were other classes that needed to QuackLikeADuck too? Sounds like a long of afternoon of refactoring and unit testing to me.

Now look at the Ruby approach:

class Human
    def Quack
        return "Ceci n'est pas un duck!"
    end
end

And that's it. No tortured class hierarchy, no tedious error-prone refactoring. Just add the method and you're good to go.

I'm of two minds about this flexibility. On the one hand, it allows you to get things done much quicker and not sweat the object-oriented design cruft that can so often pollute a design when requirements change. On the other hand, it puts a lot of responsibility on the developer to correctly anticipate ways in which their code might be used and to handle error conditions gracefully (a tall order for some developers I've encountered).

Which brings us back to the title of this entry. This has all happened before and it will all happen again (Battlestar Galactica was on last night). I would argue that for most of the 90's and the early part of this decade, strongly typed languages were in vogue (C++, Java, .NET, etc.) Right now, the hip language paradigm pendulum has definitely swung to the dynamic typing side (Ruby, Python, PHP). But for me, it doesn't really matter. Strong typing, dynamic typing, it's all the same--I'll just choose the one that let's me get my job done quickly and easily and right now that's looking a lot like Ruby.

About this Archive

This page is an archive of entries from April 2008 listed from newest to oldest.

March 2008 is the previous archive.

September 2008 is the next archive.

Find recent content on the main index or look in the archives to find all content.

Resume