Let’s talk about Python and Ruby

As a result of various things I’ve been reading up on recently, I’ve been exposed to far too many Python vs. Ruby flamewars. As someone who’s used both languages (though I’ve got much more experience with Python, and a Python-based framework is now how I make my living), I think they both solve similar problems in slightly different syntactical ways, and are pretty much equivalent on functionality.

But the debates, when they happen, always seem to come down to a few tired old arguments that just don’t hold up.

Ruby people say:

  • We’ve got blocks and you don’t.” Which seems like a compelling argument until you realize that blocks in most cases are a roundabout way of having first-class functions you can pass around, and that Python’s got first-class functions you can pass around.
  • You’re not really OO because you have to use self.” Which, frankly, I’ve never understood. Using self doesn’t magically do away with objects, classes, inheritance, etc., and self makes syntactic sense both in terms of implementing Python and in terms of Python’s “explicit is better than implicit” philosophy (since it makes scoping explicit). Plus, well, Ruby’s got the @ and @@ on variables.
  • Blah blah whitespace.” To which Python people generally reply, “blah blah end“.

Python people say:

  • We’ve got multiple inheritance and you don’t.” Which seems like a compelling argument until you realize that mixins are good enough for most cases.
  • You’re too much like Perl.” Which is just ad hominem. I’ve been guilty of this one in varying contexts.
  • We’re consistent and you’re not; look at all those redundant methods and multiple syntaxes!” Which is a philosophical difference that’s neither here nor there. Different philosophies work for different people.

So I’d like to have an honest talk about Python vs. Ruby. From where I sit, there are two things about Ruby that don’t have to do with syntax or ad hominem attacks or whatever that, were I coming fresh to the choice today, would lead me to Python over Ruby:

  1. Mutable built-in types, which are problematic for two reasons. One is that using them absolutely destroys the portability of a piece of code and makes it not Play Nice with any other components you happen to be using. The other is that it feels like the wrong solution: you’ve got an OO language, so when you want something that behaves like one of the built-ins most of the time but has this or that or the other extra thing, why not subclass it?
  2. Just mentioning a method’s name calls it. This makes it extremely cumbersome to really do higher-order programming, and can be counterintuitive and confusing. Let me talk about the method, pass it around, bind it to other names, and not actually call it until I’m good and ready.

I’d love to talk, in a civilized fashion, with a Ruby user about these issues and hear a Ruby user’s genuine points for preferring it over Python. Any takers?

Comments

Coda Hale
June 18, 2006
#

First, I like mutable built-in types. I realize that it’s frowned upon in Python circles as “monkey patching,” and that yes, abuse of this ability makes for Bad Surprises, but I think the Ruby community has developed a decent set of best practices for extending other people’s classes. In theory, a library could redeclare the Array type to be a logging object, it’s true. But then the entire community would shun that library as a Booby Trap, and its author as a Total Idiot Whose Works Should Be Avoided.

Why do I like monkey patching? Well, because subclassing is not always a good solution, for three reasons. First, you can’t make other people’s code use your subclass, so your new CGI session class is totally useless unless the framework provides a hook for that kind of thing. Monkey patching allows you to crack open any aspect of any framework and make it meet your needs.

Second, it allows you to swap out entire classes with high-performance versions. With mutable classes, you get to do cool things like make all of your DNS lookups use threads by requiring a single library. If you wanted to, you could write a wrapper to replace the slow, regular-expression based XML parser with the libxml2 bindings. If Rails’ session cookie policy doesn’t work for you, change it. And so on.

Third, sometimes you just don’t need a new class for what you want to do. It’s a lot easier to add textile rendering to the String class than it is to create a new object, pass it the string to be rendered, then render the string, then access the rendered content. happy_string.to_xhtml is just easier.

Regarding methods: you can talk about methods all you like in Ruby, you just have to keep your voice down by using symbols:

if blah.respond_to?(:wake_the_baby) blah.wake_the_baby end

Because in Ruby, blah.wake_the_baby is just shorthand for blah.send(:wake_the_baby). The method’s true name, really, is :wake_the_baby.

So while I can see why those two issues would be concerns for you, they really don’t impact my ability to write awesome software.

Now, even as a total Ruby head, there are three things that Python has which Ruby wishes it had: mod_python, speed, and Unicode support. Those are your strengths, and we envy that (just you wait until Ruby 2.0!). The arguments over meaningful whitespace and ends is just inane bikeshedding, and I’m glad you see through it.

Ian Bicking
June 21, 2006
#

Coda forgot keyword arguments; they rock, and Ruby only fakes them.

I don’t think the method thing is such a big deal. In many places where you’d pass a method/callable in Python, you have a block argument in Ruby, which is arguably more pleasant.

But the functional style of Python is a big advantage IMHO. For instance, happy_string.to_xhtml? That’s crazy! You can’t just turn strings to XHTML willy-nilly. You mean textile_to_xhtml(happy_string), or markdown_to_xhtml(happy_string), or whatever. But there’s quite a few cases of these sorts of methods in idiomatic Ruby (like Rails’ 1.year), so it’s not just a feature people never use. People really do inject trivial methods into basic types like strings and numbers and arrays.

In part it’s because Ruby people would probably write TextileParser.new().convert happy_string, which isn’t that nice. Yes, they say you can write plain functions just like in Python (and in contrast to Java and Smalltalk), but culturally it doesn’t seem to happen that often in Ruby (but I might be mistaken).

I think it’s a case the dark side of OO, where people are stuck on classes and methods and don’t appreciate plain algorithms. It also leads to frameworks out the wazoo, when Plain Old Libraries would work just fine.

Coda Hale
June 21, 2006
#

Ian, that’s a lot of assertion and few facts to back it up with. Good point on the keyword arguments, though. That said, Ruby fakes them very well, so it’s not a feature I’m hurting for. Symbol-keyed hashes do fine for now.

I’m not sure what your point is regarding strings and XHTML. If I wanted to support more than one markup for String#to_xhtml, I’d specify it: blah.to_xhtml(:markdown). It’s hardly inextensible, and you can always modify it (or, dun-dun-dun, monkey patch it) to accept whatever kind of baroque parameter set you feel is appropriate. And yes, you can turn strings into XHTML willy-nilly. Unless, of course, you’re of the opinion that the one true data structure with which to represent XHTML is the DOM tree. Me, not so much.

It’s an idiomatic construction, and to malign it as “the dark side of OO” misses the point. If you want to limit yourself, for reasons unknown, to strict functional programming, that’s fine. But what is your rationale for this, beyond distaste? And why is Python your language of choice, rather than C?

Furthermore, what is your evidence to support your claim that Ruby’s object-orientation leads to “frameworks out the wazoo?” What full-stack Ruby framework can you think of besides Rails? What other boogeyman were you thinking of? Ruby has, what, three web application frameworks: Rails, Nitro, and Camping (which is really a big prank). How many does Python have? Django, TurboGears, Zope, CherryPy, PyWork, Twisted, Spyce, SkunkWeb, WebWare… what does this say about the language? Not much, really.

Let me bring it to a point: object.length is superior to length(object) because the latter requires that a method outside of an object’s domain have intimate working knowledge of that object’s internals. The latter also requires that a single method be created which can deal with all types which could conceivably have an X dimension, regardless of unit: arrays, strings, files, football fields, baseball innings, marathons, coffins, etc.

Now, I haven’t spent much time with Python, and I’m not going to critique something I haven’t used. But it’s pretty obvious that you have only a passing familiarity with Ruby.

James Bennett
June 22, 2006
#

Ian, I have to say that your comment mostly feels like complaints about syntax and standard idioms; using methods on objects instead of calling functions is a perfectly valid way of going about things. It’s not always my preferred way (which is one of the non-language-feature-related reasons why I like Python), but that doesn’t make it wrong.

James Bennett
June 23, 2006
#

And to respond to Coda’s points in the first comment:

  1. In Python you can just as easily monkey-patch and crack open other people’s classes to make them do what you need. As you’ve noted, it tends to be frowned upon, but it’s possible and there are use cases where it’s something to consider. But that’s a big difference from being able to alter the behavior of the built-in types, which I still don’t see any use cases for.
  2. Even so, having to resort to special syntax to get a reference to a method feels kludgy, especially in situations where your code doesn’t necessarily know if it’s dealing with a method or with something else. Python’s consistency on this — the name of an object always gets you a reference to the object — is, to me, a big win.
Coda Hale
June 24, 2006
#

James, I think both points boil down to consistency. The difference between core Ruby classes and your own Ruby classes is that you probably only wrote the latter. You can monkey patch either in exactly the same manner because they are exactly the same kind of animal (a holy monkey, I guess, I don’t know).

WRT method names, in Ruby you know that any non-symbol is either a method or a variable. Period. It’s either storing something or executing something. There are no edge cases in which sometimes it’s a reference and sometimes its an invocation. It’s consistent, and I appreciate the fact that Ruby rarely causes me to pause, stare at the screen, and suck my teeth, trying to remember the history behind a particular design quirk. (Full disclosure: actually there is one of these edge cases; it’s Kernel#defined?, but it’s legacy and generally avoided.)

Let me present a fairly compelling use case for extending core classes. Rails adds the ability to render basic data types (arrays, hashes, etc.) in various useful, lightweight(ish) markup languages: JSON, YAML, and XML. Given ActiveSupport’s extensions, you can do this: render :text => Post.find(params[:id]).to_xml

I just don’t think that the potential downsides to mutable core classes (e.g., having the idiot who redeclared String to be a member of his Yarn collection get booed off the repository) really outweight the benefits of leaving them mutable (i.e., consistency, extensibility).

(It’s nice to have a civil discussion about programming languages across the aisles, James. It’s definitely a change from the usual. Thank you.)

James Bennett
June 24, 2006
#

The problem is that it’s not consistent for non-symbols, which seems like it’d be a pretty big “gotcha” for someone who’s new to Ruby but used to languages where you can pass functions around; having to remember that each time and do some sort of safe check to ensure you’re not dealing with a method (incidentally, how do you test that without accidentally calling the method?) still does feel a little kludgy.

As for the mutability, it still doesn’t feel quite right; maybe we’re getting down to the idioms of the particular languages, but when I want to convert something to a string in a particular format, I’m probably just going to write a function (and if I’m dealing with lots of different input types, probably write it to be suitably generic and do some type-checking on its input).

Maybe this is my stronger background in JavaScript showing through; I’ve been bitten by its mutable built-ins one too many times to trust that in any language (and that leads to an interesting disconnect, where Ruby people flock almost exclusively to Protoype — which does fiddle with the built-ins — and Python people flock almost exclusively to Mochikit or Dojo).

Coda Hale
June 25, 2006
#

I’m not sure what you mean when you say that Ruby isn’t consistent for non-symbols. It might be helpful if you could provide an example in Python, but in my experience Ruby is highly consistent. A symbol is an immutable string (kind of) which is used to pass messages between objects. These messages are the methods, in effect.

object.method_name(arg1) is exactly the same thing as object.send(:method_name, arg1) behind the scenes. Ruby offers some syntactic sugar there because typing send(:blah) all the time would be a pain in the ass. If you want to call an arbitrary method on an object, you send it that message:

 def say_hello(object, method_name)`
   object.send(:method_name, “Hello!”)`
 end
 blah = “blah”
 say_hello(blah, :replace)
 puts blah # => “Hello!”

If you want to capture a reference to a specific method on a specific object, you use Object#method:

 event_ref = go_forward ? gas_pedal.method(:press) : brake_pedal.method(:slam)
 event_ref.call

It may be that Python does indeed make this look kludgy; I’ve only dabbled with it for a few hours. I still think this is a fairly consistent way of dealing with methods. It’s definitely a different idea of what a method is, and coming from hard languages like Object Pascal, Java, and C#, it took a while to get used to. Now that I grok it, I love it.

Back to mutable core classes: it’s not an idiom, in the sense that it’s not the pattern, but it’s definitely an option, and it makes a fair bit of sense. There are different attitudes toward this in the Ruby community, varying mainly in how much people like Python. ;-) Like I said, I haven’t played with Python, so I’m all about the extension of classes. I have yet to run into serious architecture problems with this, even over large projects. Ultimately, it boils down to how well you write your code, regardless of where you’re putting that code, either in your own method, or in a core class.

Finally, Ruby people flock to Prototype because it’s a spin-off of the Rails project, and is used by all the fun Rails AJAX-y stuff. It’s a matter of what’s easiest to integrate, and Prototype comes pre-integrated. That said, Prototype modifies Javascript’s built-in classes because it was made by Rubyists, who definitely are down with mutable classes.

James Bennett
June 28, 2006
#

When I say Ruby isn’t consistent for non-symbols, I just mean that you have to take care to determine whether you’re dealing with a method and, if you are, you have to take care to ensure you don’t mention it in ways that will call it (at least, not until you’re ready to call it). This is in contrast to Python where you can mention a callable object and pass around references to it in exactly the same way you pass around references to anything; there’s no need for those checks because you know that until you explicitly call something, it’s not going to be called just by you talking about it.

And I do think that tinkering with classes — even the built-ins — is a Ruby idiom; in my experience, Ruby programmers almost invariably prefer adding to an existing class, while Python programmers almost invariably prefer subclassing.

Coda Hale
July 2, 2006
#

But’s it’s not inconsistent, or at least it’s melted the part of my brain which detects inconsistency.

Actually calling a method:

 “blah”.gsub(‘lah’, ‘ang’) #=> “bang”

A symbol:

 :gsub # This doesn’t actually call anything. It’s basically an immutable string.

Using a symbol to actually call a method:

 “blah”.send(:gsub, ‘lah’, ‘ang’) #=> “bang”

Getting a reference to a method:

 b = “blah”
 gsub_b = b.method(:gsub) # This doesn’t actually call b.gsub yet.

Calling the method reference:

 gsub_b.call(‘lah’, ‘ang’) #=> “bang”
 # calls b.gsub

Making a block/closure, then calling it:

 weeberse = lamdba{ |x| x.reverse }
 weeberse.call(b) #=> “gnab”

I honestly don’t understand what you mean.

And while extending built-in classes is an option in Ruby, it’s usually done with a large amount of hemming and hawing about the propriety of such an action.

And I guess you’ll just have to settle for my assurance that any reasonably-sized Ruby project inevitably has a great deal of hemming, hawing, tooth-sucking, and beard-scratching over the appropriateness of extending built-in classes. Usually, though, the deciding factor is something like “Oh my god, who cares what the Python folks think!” ;-)

James Bennett
July 3, 2006
#

I’m not disputing that it’s possible to get references to methods in Ruby. I’m not saying that it’s not possible to do higher-order programming or create closures or whatever. I’ve never disputed that.

I’m just saying one simple thing: that the default behavior of Ruby methods — which is to execute the moment you mention them — leads to an inconsistency in how you handle objects of unknown type. This is because you have to explicitly check, every time, to ensure you’re not working with a method and, if you are working with a method, you have to perform the necessary gymnastics to avoid calling it until you’re ready.

That seems to me like it’d make any sort of higher-order programming necessarily rather cumbersome in Ruby.

Mandy
July 3, 2006
#

The one simple reason to chose Ruby over Python. Rails.

When it comes to web programming Python is annoying to use. Don’t get me wrong, Python does have good web frameworks…the problem isn’t not having any, but rather the amount of frameworks available and knowing which one to use to get your project off the ground. I’m not sure how speading developmental efforts across this cornucopia of frameworks will ever allow Python to keep up to pace with the innovations of Rails?

Maybe one day the Python community will look into what they have and see if they can makes things simple by adopting a standard framework to support.

Until then, why not make life easy for you with Rails?

You can read my view and others on my latest Ruby blog entry comparing Python and Perl:

http://www.testearly.com/2006/06/30/newbie-to-ruby/

James Bennett
July 3, 2006
#

As someone who’s done web development in more than a couple languages, I have to say I don’t buy that argument, on several levels.

First, the main innovation of Rails isn’t technology, it’s marketing; all of the core technological ideas Rails uses have been done before by others, but Rails is the first project to really succeed at wrapping it up in a nice package and marketing it aggressively. And yes, I know that Python people don’t get marketing. In general we’d rather be right than popular :)

Second, I wouldn’t be so sure that Rails is the one and only Ruby web framework; more than a couple very smart Rubyists I know have recently defected to Camping, and there are other up-and-coming offerings from the Ruby community.

Third, I don’t think a monoculture is necessarily a good thing; competition leads to better results in any field, and web development is no exception. Also, I’m skeptical as to whether any open-source project can maintain a monoculture in the long term, let alone one as “opinionated” as Rails; Linux has managed to get by thus far because Linus has a generally good feel for when to impose his will and when to go with the flow (and a set of trusted lieutenants who can help make sure he maintains that feel), but from what I’ve seen DHH doesn’t seem to have that talent. I wouldn’t be surprised to see a fork down the road.

Taking all that into account when reading your entry, that seems to leave us with, um, making fun of the Python frameworks’ names as a criterion for choosing Ruby and Rails. And I’m really only interested in serious discussion of the merits of the technologies, so I’m afraid that won’t do much for me.

James Bennett
July 3, 2006
#

And posting this as a separate comment because it’s not actually a response to the points you made:

Ms. Owens, I’d appreciate it in the future if you’d consult the colloquial meanings of the term “astroturf” before commenting in that manner; posts promoting a particular product or service, coming from someone whose job description includes “creating, executing and overseeing key strategies to increase market exposure” for her company (and which fail to disclose that fact), are naturally more than a little bit suspect.

Add a comment

You may use Markdown syntax in your comment, but raw HTML will be removed. By posting a comment here, you are agreeing to the terms of my comment policy.