San Pueblan Milksnake
The very things I find ugly in Ruby are what make amazing Ruby software like RSpec possible, and that Python could never have (given the current implementation). Gary Bernhardt
I think Ruby's syntax is ugly until they do something glorious and beautiful with it like Rake, then I think "damn it!" Unidentified Attendee
Q: Does Ruby have a better inheritance system than Python?
A: I find inheritance distasteful. That's like asking me whether I would rather eat grasshoppers or tree bark. Gary Bernhardt
My perspective on Ruby was rocked last weekend at Northwest Python Day in Seattle.
Compared to other programming languages, Python and Ruby are so similar that hearing arguments between them is like trying to figure out which identical twin is more handsome.
But there are differences, and not only in the languages! The Seattle Python community represented there was almost painfully polite. People sat silently for 3-5 minutes between talks while the speaker plugged in to the projector. I rudely whispered to my neighbor, eager to discuss the ideas presented.
During the talks, people mostly sat and listened to the speaker. Many Ruby conferences in the USA are half full of hackers with laptops open, coding away at a project and jeering the speaker over IRC. Not here.
I later learned that this may be an attribute unique to Seattle Python developers. After all, we’re the city that gave the world the Uptight Seattleite and where drivers with the right-of-way stop to let cyclists turn left in front of them.
The main attraction (and last presentation) was from my friend Gary Bernhardt1. Gary spent a few months contracting on a Rails project for four hours in the morning and a Django project for four hours in the afternoon. So when he speaks of either, it’s not in ignorance.
At the end, I couldn’t find a single criticism of Ruby that I disagreed with. I did pick up a new appreciation for unique features of the Ruby language that I previously took for granted.
There was an audible gasp from the audience when Gary posted a slide with this quote from Matz:
Ruby inherited the Perl philosophy of having more than one way to do the same thing. I inherited that philosophy from Larry Wall, who is my hero actually.2
I’m not sure if the shock was from hearing someone embrace TIMTOWTDI or from learning that someone considers Larry Wall to be a hero.
If you open a python prompt and type
>>> import thisyou’ll see the Zen of Python, 19 guidelines that Python programmers live by. One is “There should be one—and preferably only one—obvious way to do it.”
I do appreciate that Ruby is flexible enough to be used in many ways. That’s what makes the creativity and richness of Ruby libraries like Hpricot and Rake possible.
Gary pointed out that in most languages, appending parentheses to a variable that references a function, calls the function.
my_method = Proc.new { ... }
# ERROR: You can't do this in Ruby
my_method()In Ruby, parentheses are a vestigial appendage. You could write an entire Ruby application without parentheses (and some do!). To Python developers, this is shameful.3
# In Ruby, you must use the call method
my_method.call()I’m on the line here. I always use parentheses when defining methods, but use them infrequently when calling methods.
One’s aesthetic reaction to the following code snippets distinguishes the Rubyist from the Pythonista:
attr_accessor(:title, :slug, :body)
belongs_to(:article)
before_save(:encrypt_password)
get("/articles") do
haml(:article)
endAnd yet our repulsion at too many parentheses contrasts with Lisp’s influence on Ruby (which will be mentioned later).
It’s well known that Pythonistas abhor monkey patching, a.k.a. duck punching,4 i.e. adding methods to existing classes.
They have a point. You can do a lot of damage in a few
lines. Previous versions of ActiveRecord monkey-patched
Logger, making it almost unusable for other applications that
expected it to work in the standard way (it now behaves more civilly).
But without monkey patching, you can’t insert should and
should_not into Object:
describe MySum do
it "performs a sum" do
sum = MyClass.add(1, 1)
sum.should == 2
end
endSo Python can’t implement the smooth syntax of RSpec.
The last line of the Zen of Python is
Namespaces are one honking great idea — let’s do more of those!
Python’s import statement gives you a high level of control
over what code you’re bringing in to your application.
from django.db import modelsIn contrast, a Ruby require often slurps in other classes that
seem entirely unrelated to the code you requested.
require 'active_record'
# => ActiveSupport is now in the global namespaceHaving more control over the global namespace would be useful, but it seems to me that it would require a re-architecting of nearly the entire Ruby language.
My favorite quote of the whole presentation:
Ruby takes full advantage of the Lisp chainsaw.
Can I have that on a t-shirt?5
Python has no comparable equivalent to Ruby’s do end
block. Python lambdas are limited to one line and can’t
contain statements (for, if, def, etc.). Which leaves me
wondering, what’s the point?
Without powerful blocks and lambdas, you can’t have Rake. You can’t have RSpec. You can’t have Sinatra’s clear REST syntax.
Without yield, you can’t have
RailsCamp.
So the very features that make Ruby complicated to parse and potentially Perlish are the same features that can make it incredibly descriptive and flexible.
Not to mention the free Lisp chainsaw without all the parentheses!6
A Hacker News discussion about this article has over 200 comments here.
1 Gary blogs at blog.extracheese.org and recently posted the audio and slides of the talk.
2 From an interview at Artima
3 You can also call a Ruby proc with square brackets, but the crowd found that even more revolting.
4 Wikipedia on duck punching
5 Or just Lisp Chainsaw Massacre. Apparently this is a reference to Perl as a “Swiss army chainsaw.”
6 Personally, I don’t mind the parentheses. They can be rather comforting like a pillow.