Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
Vying Games : Blog : Ruby and Trust
[go: Go Back, main page]

Play other people online: login | register

Vying Games : Blog

Ruby and Trust

May 25, 2007 at 04:00PM
In Ruby, Software Development
By eki
1 Comments

Few things help an individual more than to place responsibility upon him, and to let him know that you trust him.

-- Booker T. Washington

Why I Love Ruby

When I got up this morning I felt particularly inspired to write an article -- no, an ode -- to the Ruby programming language. I think this is what happens when a programmer finds a language that he or she feels really in tune with. To stretch the analogy, programmers are all tuned to different frequencies, and, if they're lucky, they eventually find that one language with which they really resonate with. Which is probably why there are so many "Language blank is the Best Language Ever!" articles strewn about the internet.

Anyway, I sat down this morning and started making a list of all the reasons I love Ruby. I didn't get too far before I realized that the full list would be too much for one article, so I've decided instead to focus on just one element of my Ruby love affair: Trust.

Ruby trusts programmers.

Open Classes

As an example of that trust, Ruby has open classes. Any developer can open any class and modify it in whatever way he or she feels like. This can mean adding new functionality, redefining existing methods, or even removing functionality (think sandboxes).

Let's say I'm developing board game software. I'll be dealing with board coordinates quite a lot. One natural solution would be to create a Coordinate class with, say, x and y accessor methods:

class Coordinate
  attr_reader :x, :y

  def initialize( x, y )
    @x, @y = x, y
  end

  def to_s
    "#{(97+x).chr}#{y+1}"
  end
end

This Coordinate class could be used something like this:

>> c = Coordinate.new( 3, 4 )
=> #<Coordinate:0xb7919440 @y=4, @x=3>
>> puts c
d5
=> nil
>> c.x      
=> 3
>> c.y
=> 4

There are two ways we want to use a Coordinate:

  1. As simple container of x, y pairs
  2. We also need string representations, like "a1" for (0,0) because these strings are often used in game notations

Don't worry too much about the implementation details, what I really wanted to point out is an alternative:

class Symbol
  def x
    to_s[0]-97
  end

  def y
    to_s =~ /\w(\d+)$/
    $1.to_i-1
  end
end

This code allows us to use Ruby's symbols as Coordinate literals, like so:

>> c = :d5
=> :d5
>> puts c
d5
=> nil
>> c.x
=> 3
>> c.y
=> 4

Ruby allows us to open it's Symbol class (if you're not familiar with Ruby's symbols, think of them as immutable strings that instead of being represented within quotation marks, are prefixed with a colon). In this case I've modified Symbol to provide the same interface as Coordinate, effectively overloading Symbol literals so they can be used as Coordinate literals. I, as a programmer, might decide that this trick is worthwhile because it leads to cleaner, more expressive code.

There are, of course, negatives to open classes. What if some library I depend on decides to add an x method to Symbol that behaves differently than mine. Such conflicts would no doubt lead to bugs (and potentially hard to find ones at that). Obviously, redefining methods or removing methods could lead to other compatibility problems. Some programmers refer to he process of opening and modifying classes as monkey patching, which should be a fairly strong hint that some programmers disapprove of the technique.

So, when should you take advantage of open classes? Really it's up to you. Maybe you're a more conservative programmer and only see benefit in making temporary bug fixes to third party libraries. Or, maybe you're a huge fan of Ruby Facets. Either way, Ruby allows you to draw your own line in the sand.

Not every programming language will trust you to modify its core classes. I suspect more OO languages have closed classes than open classes. Some, like Java, take it a step further: The Java final keyword is often used to prevent programmers from even subclassing its core classes.

Duck Typing

When defining a method, it's considered idiomatic in Ruby not to check the class of parameters. For example, the following is not encouraged:

def neighbors( c )
  raise "Not a Coordinate" unless c.kind_of?( Coordinate )

  [Coordinate.new( c.x + 1, c.y ),
   Coordinate.new( c.x - 1, c.y ),
   Coordinate.new( c.x, c.y + 1 ),
   Coordinate.new( c.x, c.y - 1 )]
end

With the above code, neighbors won't work when given one of the cool symbols we created earlier. Technically, all neighbors needs to work is an object that responds to x and y method calls. So we could rewrite the above like so:

def neighbors( c )
  unless c.respond_to?( :x ) && c.respond_to?( :y )
    raise "Not Coordinate-like"
  end

  [Coordinate.new( c.x + 1, c.y ),
   Coordinate.new( c.x - 1, c.y ),
   Coordinate.new( c.x, c.y + 1 ),
   Coordinate.new( c.x, c.y - 1 )]
end

Of course, we could just chill out and write it like this:

def neighbors( c )
  [Coordinate.new( c.x + 1, c.y ),
   Coordinate.new( c.x - 1, c.y ),
   Coordinate.new( c.x, c.y + 1 ),
   Coordinate.new( c.x, c.y - 1 )]
end

In which case, garbage-in would cause an error like this:

>> neighbors( {} )
NoMethodError: undefined method `x' for {}:Hash
        from (irb):26:in `neighbors'
        from (irb):35

I gave neighbors an object that doesn't provide a Coordinate like interface (an empty Hash) and got a reasonable error. In fact, I'd probably prefer the default error message above to either of the error messages in the previous examples.

By relaxing, and not worrying about type checking parameters, I'm trusting future programmers to pass valid coordinate-like objects. This is duck typing: if something walks like a duck, and quacks like a duck, why should I care whether or not it really is a duck?

Ruby's dynamic typing and duck typing philosophy allow programmer creativity and inventiveness. For example, creating proxies and mock objects in a duck typing environment is a breeze.

None of this would be possible without trust.

Happiness

Ruby's creator, Yukihiro Matsumoto (Matz), has said many times that one of the biggest objectives of the Ruby programming language is programmer happiness.

I think a lot of happiness is achieved through Ruby's expressive syntax. For many, Ruby code is easy on the eyes, and there's a certain joy in writing elegant code.

But for me, happiness comes from freedom. There is nothing more frustrating than working around restrictions inherit in your programming language. Sometimes the restrictions are tolerable because they're a part of a trade off. Erlang, for example, places restrictions on its programmers, but those are for the sake of providing a high degree of parallelization and fault tolerance.

But some languages, like Java, are restrictive due to a lack of trust. Sometimes I wonder if the Java motto isn't: Remove all the sharp tools from the toolbox before some programmer pokes his eye out.

I prefer my toolbox fully stocked. I prefer a relationship built on trust. And, that's why I love Ruby.

Comments

Nicolás Sanguinetti says,

Hear hear! :) Agree 100% with what you say.

It's a pity most programmers don't want to be trusted by their languages and prefer to bloat their code "just because" it's the right thing to do.

Got Something to Say?