I like Python! No, wait, Ruby! No, but Python…

I hate Ruby.

There I was, just buzzing along, using Python, satisfied that I knew one of the coolest languages around, and definitely the coolest language you could actually get a job with.

Then Ruby came along.

It was English-like. It was practical. It was fun. It was possible to get a job with it. It was even Japanese.

Almost everything about Ruby’s syntax, language features, and library just made sense to me almost immediately. I don’t mean I immediately understood the entire language—blocks took me quite a while to understand, and metaprogramming can be pretty confusing for almost anyone, unless you’re Dan Friedman. I mean that I almost immediately saw, on some intuitive level, why Ruby had been designed the way it was. My mind easily fell into thinking the way Ruby wanted. Even when I barely knew the language, I could figure out just where to bust out a line-ending if or while. I immediately loved string interpolation.

By contrast, there are plenty of things in the Python language and libraries that, to this day, do not make sense to me. I learn them by memorization and use them. Examples are len being a function instead of a method, and the entire os.path module. Once, I needed a quick script to process a few files in some nested directories. I’d only been learning Ruby for about three days, and I wanted it done quickly, so I used Python. I struggled for an hour to write a Python program using os.path.walk, and finally decided to look into how Ruby would do it. I had the thing done in Ruby in ten minutes.

Other parts of Python make sense in a tortured sort of way, like writing ' '.join([1, 2, 3]) instead of, say, join([1, 2, 3], ' ') to join some strings. Yeah, fine, I understand that it makes some kind of sense to think of it as “String ‘ ‘! Join the list [1, 2, 3]!” rather than “List [1, 2, 3]! Join yourself using the string ‘ ‘!”. Just like it makes some kind of sense that C++ uses virtual void flagellate(int flagellant) = 0; to declare a pure virtual function, since you’re initializing the function’s pointer to null. But just because they kind of make sense when you think really hard about them, that doesn’t make them obvious (like the Zen of Python claims to want), and definitely doesn’t justify the kind of condescending blather that the Python FAQ presents in response to the question of why it’s ' '.join([1, 2, 3]).

Maybe it’s because I started as a natural language guy, in English and linguistics, that Ruby makes so much sense to me. People complain that it has too many different ways of accomplishing the same thing. It definitely flies in the face of Python’s “There should be one obvious way to do it”. On the other hand, Python itself frequently chooses ways to do things which aren’t incredibly obvious, like the aforementioned os.path.walk. That there is only one way to do it, if anything, just makes the non-obvious nature of that way hurt even more, because there’s no way to get out of it.

To me, Python is designed like a formal system, while Ruby’s design is more like a natural language. Of course, there are languages much more like formal systems than Python; there’s Lisp, and Haskell, and Scala. Even Java, once you stop looking at syntax and warts like arrays and primitive types, and just look at its efforts to be “objects all the way down”. But Python seems to have aspired to the same kind of minimalism as Lisp. Python aspires to rid itself of synonyms and homonyms, to make all antonyms expressible by sticking “not” at the front. Ruby takes on extra syntactic baggage like “unless” and suffers methods with different names that do the same thing, in the name of being more obvious and readable to humans. Python believes that explicit is better than implicit. Sometimes you have to write more for the sake of making it explicit. Ruby tolerates weirdnesses like the flip-flop operator, which does a boatload of implicit stuff behind the scenes, on the principle that it’s fairly common to do something like that, so you can memorize one set of implicit behavior.

Please don’t take this to mean I think Ruby is better than Python. I said that Ruby, the language, and several of the libraries, fit my style of thinking better than Python. And even more, please don’t take this to mean that I think crazy things like Perl’s implicit $_ variable are okay. Ruby, I think, gets just the right amount of redundancy and implicit behavior; if Python has too little, then Perl has far, far too much. Ruby, I think, gets just the right distance between natural language and formal system; Scheme and Haskell are a little more like a formal system than I prefer, but Cucumber and AppleScript get much closer to natural language than they should. Cucumber and AppleScript are wonderfully easy to read, but horrible to write, while Scheme and Haskell are nice to write, and the end product is very aesthetically pleasing in a classical sort of way, but can be rather hard to read.

So, given that I liked the Ruby language better than the Python language, why haven’t I dumped Python yet? Third-party libraries.

Ruby has amazing libraries related to the web. Rails has been extensively talked up. I made another visit to it after I put some serious effort into learning Ruby, and I found it a lot more pleasant than I remembered. By contrast, I started to find Django code-y and somewhat laborious. Sinatra is also well-liked. Nokogiri blows away any HTML/XML processing library anywhere that I can think of; Python’s Beautiful Soup is great for scraping HTML, but less useful for XML, while Clojure’s Enlive is great for scraping and templating, but again, less useful for XML than Nokogiri. Let’s not even talk about Python’s standard library HTML and XML parsers; the HTML parser is a horrible Java-wannabe library that makes you implement your own API, while the XML parsers are cumbersome and limited.

Ruby’s libraries in other areas that are of interest to me are less impressive. Python has NumPy, SciPy, Natural Language Toolkit, BioPy. Clojure puts in an appearance with Incanter and OpenNLP, and there are some Java libraries here that you can use. Ruby has basically nothing in scientific computing, NLP, or bioinformatics. It could be great for all of these areas, because they’re all interdisciplinary, and I suspect that people from outside math and computer science would find Ruby easier to just get than other languages, the same way I did. But the Ruby community’s main concern is the web, and it shows.

If Python were a bad enough language, I wouldn’t feel so conflicted. But Python’s not a bad language; it’s a great language with a few annoying facets, most of them superficial. I like Ruby the language better, but Python’s sure better than a cup of hot Java in the face, to say nothing of being skewered by a C++. Most of the libraries I want are in Python. Some are in Ruby, but the ones that aren’t are huge ones that aren’t likely to be replicated in Ruby any time soon. Python is, in fact, probably the only language that has all the libraries you could ever want, is pretty nice to read and write, and can also get you a job. And I would have been happy with it forever, if that stupid Ruby hadn’t come along.

But I’m also mad at Ruby for creating this internal conflict for another reason: if I had never learned of its existence and just committed to Python, maybe I would know it well enough to have a job by now.

Stupid Ruby.

Advertisements