Don’t Fight Ruby!

In this blog post, “Adding Type Checking to Ruby,” I spit up a little bit in my mouth. I don’t want to be harsh… I can appreciate Daniel’s different approach to Ruby.

Mind you, I’m no Ruby whiz (yet), but look at this example:

def create_name(fname, lname)
  raise "fname must be a String" unless fname.kind_of? String
  raise "lname must be a String" unless lname.kind_of? String

  fname + " " + lname
end

I really couldn’t get past this first example. We’re seriously in dire need of checking that a first and last name are passed in as String types? I know it is just an example, but it is a lousy real-world example. It’s a bit like saying we need TDD and proceeding to show test-driving an accessor. I need more red meat!

Okay, so then there is some “Type” library that bundles up some helper methods to make the ugliness above go away:

typesig String, String
def create_name(fname, lname)
  fname + " " + lname
end

Except, the ugliness didn’t go away because it is the concept that I find hard to embrace.

I think the author is attempting to coerce Ruby to act as if it was statically typed. Static typing can be a helpful crutch, but more often than not, it just gets in the way. And if you like it that much, then use a language where it is built-in already. But certainly don’t pollute your Ruby code with pseudo-static type checking on every method!

I would advise against focusing on that static typing retro-fit effort.

In doing Ruby development, I have found it better (and easier) to put your labor into:

  1. writing clear, object-oriented code
  2. driving your code test-first (BDD/TDD)

Let your tests tell you things are working right. For example, if you wanted to ensure the class that had the first and last name in it behaved properly, you could write RSpec tests:

it "should show patient name as 'first last'" do
  @patient.name.should == 'John Johnson'
end

And if you really wanted to test that the values were Strings you could protect the creation of that object. For example, in a “validate” method that ensures that if a “Setting” instance is to represent a Boolean, than its value better be set to “true” or “false:”

# Settings to be used in the program
# Access in the program as follows: s("Company")
class Setting
 include MongoMapper::Document
...
 # Attributes ::::::::::::::::::::::::::::::::::::::::::::::::::::::
 # What the user sees as a label
 key :label, String, :unique => true, :required => true
 # How we reference it in code
 key :identifier, String, :unique => true, :required => true
 # Data type
 key :field_type, String,  :default => "String"
 # Store any kind of object in the value field.
 # This is what we will use to substitute for the setting
 key :value, Object,  :serialize => true

 # Validations :::::::::::::::::::::::::::::::::::::::::::::::::::::
 validates_inclusion_of :field_type,  :within => FIELD_TYPES
 validate :validates_as_boolean

  def validates_as_boolean
    if field_type == 'Boolean'
      errors.add(:value, "must be set to true or false for Boolean type") unless value == true || value == false
    end
  end
...

Adding a contract into every method as a matter of course, is just a waste of time and a polluting of the code “forest.”

IMHO, let Ruby breathe the freedom it was meant to have.

2 thoughts on “Don’t Fight Ruby!

  1. Daniel Spiewak

    I doubt we’re going to come to an agreement here, but I would like to point out that I’m *not* new to Ruby in any sense of the word. I’ve been working with Ruby for almost 7 years. That even predates the popularity of Rails! So, I’m not speaking from a place of ignorance here.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.