stevengharms.com

Sententiae viri ex temporibus duobus

A Handy Tip for Ruby Developers: Dispatch Tables

If you are learning Ruby coming from a C or Perl background, you may find the following discussion somewhat interesting.

One of the most common idioms ( especially in my CGI programming in Perl ) is to use a dispatch table based on a keyword or an option.

It is particularly handy to arrange a hash such that by entering a ‘keyword’ (the key in the hash element) you trigger an anonymous subroutine, or a pre-defined routine.

The code for this, in Perl, looks something like this:

my %h = ( 'alpha' => &theAlphaRoutine;, 'beta' => &theBetaRoutine;, 'gamma' => &theGammaRoutine;, );

Thus by dereferencing %h with the argument ‘alpha’ you can achieve the same net effect as if you had called theAlphaRoutine()

Thus:

$h{'alpha'}()

is equivalent to

theAlphaRoutine()

Now, let’s sexy it up a tiny bit. Let’s put anonymous subroutines inside the hash, that is to say, routines that are defined as you define the structure of the hash. This creates that very cool Perl effect of “I’m running code that I didn’t define in subroutines, this is very subversive.”

my %h = ( 'alpha' => sub { print "first comes first\n"}, 'beta' => sub { print "second comes second\n"}, 'gamma' => sub { print "third comes third\n"}, );

Nice! Thus executing:

$h{'alpha'}() will generate:

first comes first

As an experiment, let’s create an array of the names of the key values in %h and iterate through them. We will then us the current keyword (held in the $_ variable) to dereference the hash. In so doing we will ‘call’ the associated anonymous routine.

The code:

#!/usr/bin/perl

@names = qw |alpha beta gamma|;

my %h = ( 'alpha' => sub { print "first comes first\n"}, 'beta' => sub { print "second comes second\n"}, 'gamma' => sub { print "third comes third\n"}, );

for ( @names ){ $h{$_}()}

Produces the expected result of:

first comes first
second comes second
third comes third

Well, that’s just great. Now, I thought that I would like to try this idiomatic expression in Ruby. Now, let me first say that doing things this way is very un-Ruby like and can be done in many superior implementations. This is simply a thought experiment for you.

Let’s start with this basic Ruby code:

names = %w(alpha beta gamma)

h[:alpha] =lambda { puts "first comes first"} h[:beta]= lambda { puts "second comes second"} h[:gamma] = lambda { puts "third comes third"}

So the goal would be, “run through the listing of items in ‘names’ and then proceed to dereference these anonymous subroutines lambda expressions[1] using the Proc.call method”

First Attempt

A naive stab ( that is, my first stab ) looks like this. We add a simple command to spit out the names that we’re going to use for the dereferencing.

names.each {|name| puts name}

This produces:

    NameError: undefined local variable or method
       `h' for main:Object

What?! An important thing for Perl coders to remember is that Ruby doesn’t auto-vivify[2] a hash by declaring one of its key value pairs. You have to create a pointer to a Hash object before you can start adding keys and values. So let’s add this line before the h[parameter] declarations:

h = Hash.new

Produces:

alpha
beta
gamma

OK, so now we have the keys, let’s get those keys to dereference within the Hash and we’ll see our lambda expressions performed.

Second Attempt The naive next step would be this:

names.each {|name| h[name].call}

Which produces:

    NoMethodError: undefined method `call' for nil:NilClass
    at top level    in goofing.rb at line 9
    method each in goofing.rb at line 9
    at top level    in goofing.rb at line 9

What the heck?

My first instinct was that it was a scoping issue. “Ah-hah!”, thought I, “I need to declare h to be $h” ( keep in mind Perl-ers, $x merely means “make it global”). This made no difference.

What happened is something that I hadn’t caught on to:

:symbolname is not equal to ‘symbolname’

This should be obvious, but it’s so obvious, it’s easily missed.

One simple set of commands should demonstrate this:

puts :symbolname.class puts 'symbolname'.class exit

Produces:

    Symbol
    String

In our example, thus, names contains strings - not symbols. Our hash structure is set up using symbols as keys.

I thought that perhaps the fix was to simply put a : in front of each of the strings within names. But again, this merely nets you a collection of strings with a “:” as the first character.

A quick bit of code makes the point clear:

names = %w(alpha beta gamma) names2 = %w(:alpha :beta :gamma) names.each {|name| puts name.class} names2.each {|name| puts name.class}

It’s just a bunch of strings!

OK, so we need a method whereby to convert strings to symbols. Fortunately String#to_sym does just this.

Thus, to get whan we expect, let’s add:

names.each {|x| $h[ x.to_sym].call}

In sum:

names = %w(alpha beta gamma)

h = Hash.new h[:alpha] =lambda { puts "first comes first"} h[:beta]= lambda { puts "second comes second"} h[:gamma] = lambda { puts "third comes third"}

`names.each {|x| $h[ x.to_sym].call}

And this gets the desired:

first comes first
second comes second
third comes third

Of course the really Ruby way of running this would be…

h.keys.each {|x| h[x].call}

Or better yet…

h.values.each { |y| y.call}


  1. ‘Lambda Expression’ is a particularly interesting area in the hop from pure mathematics to computer science and the reader is highly encouraged to look into the basic outlines of the design of Alonzo Church and colleagues. Defmacro.org has a really great summation. I’ve only read some of the basics in my on-and-off flirtation with Lisp. Check it out.

  2. Auto-vivify means “auto make alive”. By creating a hash key value pair $aHash{'foo'} = 'foomonkey', %aHash is auto-vivified, automatically created. Not so in Ruby.

Comments