Making Ruby Quack—Why We Love Duck Typing

Tweet

One of the most lauded features of Ruby is its support for the technique known as duck typing. Despite the humorous name, duck typing enables Ruby developers to write succinct, clean, and readable code with very little effort—all the things that attracted us to Ruby in the first place.

“What would Duck Dodgers do?”

The basic premise of duck typing is simple. If an entity looks, walks, and quacks like a duck, for all intents and purposes it’s fair to assume that one is dealing with a member of the species anas platyrhynchos. In practical Ruby terms, this means that it’s possible to try calling any method on any object, regardless of its class.

To demonstrate, here’s a simple scenario: you’re working with a system that tracks shipping manifests for each container on a ship. Data for each manifest is easily obtained through a RESTful web service:

# Fields:
# * package_count: number of packages in manifest (integer)
# * mass:          total mass of all packages, in kilograms (BigDecimal)
class ShippingManifest
  attr_accessor :package_count, :mass
end

Each ShippingManifest tracks the number of packages, as well as the combined mass for all the packages. Your task is to take a set of manifests (provided in an Array) and give a summation of the total mass and the total number of packages in the set.

“You say the Loch Ness Monster is living in your jacuzzi?”

The problem looks simple enough, right? We could probably bash it out in nine or ten lines of code:

def summarise_manifests
  # Grab the latest set of Manifests (returns an Array)
  manifest_set = ShippingManifest.all

  # Initialize our counters
  total_package_count = 0
  total_mass          = BigDecimal("0")

  manifest_set.each do |m|
    total_package_count += m.package_count
    total_mass += m.mass
  end

  return [total_package_count, total_mass]
end

Easy, but verbose. It’s possible to reduce this method to a single line of code:

def summarise_manifests
  ShippingManifest.all.sum
end

How? Duck typing!

“Skeleton key, eh? Where did you pick that up?”

Our one-line solution takes advantage of one of Ruby’s most powerful features: the Enumerable module. Enumerable provides an API to easily manipulate collections of objects—most commonly Array and Hash.

Specifically, we’re using a method defined in ActiveSupport: sum, which is available in every Rails project. As the name suggests, it’s used to calculate the sum of a set of objects. It does this by using inject, a function that uses a block to reduce a set of objects to a single value. In the case of sum, it does this by using the addition operator.

This makes perfect sense when we’re talking about numbers: 1 + 1 = 2 is fundamental arithmetic. And it’s also easy to see how it can be used to concatenate strings together:

['foo', 'bar', 'baz'].sum # => 'foobarbaz'

But how does Ruby know how to add two instances of our ShippingManifest class?

Easy. We tell it how.

“That’s only part of who I am. I’m actually quite complex.”

“Everything is an object” is one of Ruby’s core mantras. The implication of this is that (nearly) every operator is actually a method call on an instance of an object—which grants us the ability to define our own implementation for that operator. It means we can do this:

class ShippingManifest < ActiveResource::Base
  # Returns a new instance of ShippingManifest containing the
  # sum of both `mass` and `package_count`
  def +(other)
    self.dup.tap do |neu|
      neu.mass          = @mass + other.mass
      neu.package_count = @package_count + other.package_count
    end
  end
end

By adding a few simple lines of code, we’ve added functionality that has made the potential ShippingManifest much more usable by the wider Ruby library.

“And let me remind you again, folks, that you’re listening to Truth or—Aaaugh!”

Implementing custom behavior for the + operator only scratches the surface of what’s possible through duck typing. There are other operators that have custom behavior implemented: &, *, -, <<. Even == can have custom behavior. Need to sort a set of records? Implement the comparison operator (<=>) in your classes and you’ll be able to take full advantage of the sorting methods provided in Enumerable.

As always, some example code for this post is available on our GitHub account—feel free to fork it and experiment! For starters, try implementing behaviour for - and <=>, then experiment with the methods in Enumerable.

If you have queries about any other aspect of Ruby, feel free to ask in the comments!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.dailygrind.it Sergio

    Link to Dan’s website is wrong I think…

  • Dan Cheail

    Nah, I don’t have one. ;)

  • http://mwilden.blogspot.com/ Mark Wilden

    Good article. Anas should be capitalized, though (I’m working on a taxonony database).

  • http://paddy3118.blogspot.com Paddy3118

    It is nice to see an article on DT but it would be nice if you pinpointed what, out of all that you do do, is DT.

    After recognising that you need to describe ‘+’ for your class, duck tying is the ability of the sum method to work on any class that knows how to ‘+’, regardless of the classes hierarchy at the time it is invoked.

    No need to inherit from integers or whatever; no need for ‘+’ to be around from initial class definition, and ‘+’ could well be deleted after you have made use of it – Duck Typing is dynamic.

    – Paddy.

    • Dan Cheail

      Duck typing and the dynamic nature of Ruby are two separate features; the two have no bearing on each other.

      • http://paddy3118.blogspot.com Paddy3118

        The dynamism helps distinguish DT from, say, structural typing.

  • http://mwilden.blogspot.com/ Mark Wilden

    More generally, “duck typing” follows from calling methods by sending messages. In this sense, Smalltalk and SCI (the Sierra On-Line language I worked on in the 90’s) are also duck-typed.