Hacker News new | comments | show | ask | jobs | submit login
What's better about Ruby than Python? - comp.lang.python (groups.google.com)
205 points by pbjorklund on Aug 26, 2010 | hide | past | web | favorite | 127 comments

The title made me nervous that this would be run-of-the-mill fanboy garbage. But I trust the author (Alex Martelli), so I read it, and I'm glad I did. It's reasonable and sane.

My two big take-away points:

1) It's very nearly a wash. Ruby and Python are so close together (beneath any superficial differences) that distinguishing is almost an exercise in futility.

2) The key thing about Ruby he doesn't like is its "TOTAL, unbridled "dynamicity", including the ability to "reopen" any existing class, including all built-in ones, and change its behavior at run-time." To M's way of thinking, this makes Ruby less "suited for use in large production applications" and more suited for "tinkering."

I don't buy the argument myself, but I will admit that when I first started learning about Ruby, everything I read went on and on about how cool it was that you could open everything up and play. But as I read more, I discovered that the community seems pretty heavily tilted against monkey-patching now. (I may be wrong about this, but it's my impression.) I don't think that means Ruby is for tinkering only, but it does suggest that Martelli's concern is legitimate.

He also offers some nice pasta recipes along the way. (So much so, that now I'm hungry.)

We run probably one of the largest Ruby production systems that has ever been made and the dynamism and "monkey patching" are used daily to skillfully make the language better and better over time for the tasks that we solve.

It's absolutely crucial to teams that internalize it's power and pitfalls.

"one of the largest Ruby production systems that has ever been made"

As an aside, I worked on a very large Ruby app (built by about a dozen developers over the course of years) and then later worked on another very large Ruby app of similar size. One of the employees there remarked that it was the largest Rails app built. I think the people who write very large Ruby apps tend to be very quiet about it (no blogs, no open-source, less community participation, etc.) so that if you're one of the ones writing a very large Ruby app you think yours is the largest. :)

I can fully agree about the quietness of developers working on large ruby app considering I work on such an app but were not big on advertising it yet.

Lol, I work on a Ruby app that is entirely internal and processes an incredible volume of information. The key here was the ease of adding a few key C modules.

I was about to ask if you work for Twitter, but then I read your bio. I like Shopify, all the shops built with it look real slick.

Oh, and I like delayed_job as well.

It's an extension of the critique leveled at dynamically typed languages by Java/C/C++ devs. How can you possibly work in a world where function foo can return/expect any type?

The more dynamic (powerful?) the language the more leverage good, experienced developers have. They can achieve results in 10 lines of code that are hard/impossible in more static languages (closures, meta-classes etc.). The other edge of the sword is that one dud developer can bring your whole stack down by being too smart for their own good. The more dynamic the system the greater the risk. In Ruby as is pointed out you can have a developer on the other side of your code base royally screw you because he wants feature X of sequences to act differently.

As with all things it's a balancing act, how smart are your devs, how much time can you spend testing and how strict are your coding standards.

I can believe that such power can be achieved and scale within a single department-size group of "smart people".

The problem appears if or when you want to share the code between departments or give it to the world. Give "clever code" to someone who simply doesn't understand the whole process and they turn into a "dud developer" no matter how otherwise intelligent they are.

(And what's with down-voting the parent? It's a fair point even if I'm not in agreement)

Cool. What's the site?

The fact that anyone at any point can monkey patch any built-in is a potential liability for large teams, with average programmers. It must be addressed by culture and code review that you may or may not have.

Still worth it though :)

I've never bought the idea that the opposite is a better trade off because it comes with costs too. Have you ever worked with a bunch of mediocre java programers? Heaven forbid they make an interface that isn't well thought out and needs to be changed later!

That extreme is also terrible. Python is somewhere in between.

if ruby is an extreme to the left, and java an extreme to the right, then python is also extreme to the left. The difference between python and ruby in dynamicity is really small.

Python is not in the middle of ruby and java. Python is virtually on top of ruby.

A fair point. Maybe 'optimum' would be a more useful concept here than 'centre'... I see Python's position on the dynamicity (ugh) spectrum is in line with it's philosophy on readability.

(On a side note - your definition of 'extreme' rather depends on what outliers you have. I do love it when the US media refers to the Democrats as 'left of center' ;)

yup, republican and java are in the same camp ;)

On a serious note, I've been doing ruby as my job for about 5 years and I work at a giant corporation with a few hundred programmers. In those 5 years I've never been bitten by someone changing a core class that breaks my code. Maybe 3 times I've been bitten by someone changing a non core class, and it usually takes me about an hour or two to resolve it.

So that's maybe 6 hours in 5 years where I've lost time due to this, but the amount of time it has saved me is far larger than this.

That sounds like an interesting big Ruby gig that would be a good story to tell the anti-dynamic languages FUD brigade.

You should blog :)

In what situation is it actually a good idea to employ a large number of bad programmers, rather than a small number of good ones? We shouldn't be making trade-offs for the sake of bad ideas ;)

There's a distinction to be made between preventing risky behaviour and relying only on a programmer's good judgement to avoid mistakes. Even if your team is solely good programmers there's a lot to be said for discouraging unsafe behaviour by default since anyone can have a bad day, distraction, etc.

I view this situation as somewhat analogous to having guards on power tools: even a highly skilled operator doesn't want to rely on always doing something perfectly.

The nice thing about code is that (when version controlled) all mistakes can be fixed retroactively. As long as two programmers aren't having a bad day at once, pairing will catch "all" errors. As long as all your programmers aren't having a bad day at once, code review will catch "all" errors again. ;)

This is why you need to automate peer review http://www.parasoft.com

...and all it takes is one rogue or bad programmer to mess up the code base.

...in either scenario. During code review, however, I'd expect that the good programmers would reach much more strongly to the rogue commits than the mediocre programmers would.

Are there many ruby app built by large teams?

In my experience, teams building Ruby apps tend to be much smaller than teams building Java apps ..

That said, this is a problem for any libraries that you might want to include, you have to trust that the authors followed conventions and didn't do any thing 'evil'. Ruby Rewrite (http://rewrite.rubyforge.org/) was conceived to specifically avoid this sort of problem.

The best software is built by small teams.

Both languages offer the unvigilant programmer plenty of opportuities to shoot off his foot. If you're really concerned about this you should take a look at scala. I'm amazed at how much easier it is for me to maintain my own scala code than either ruby or python.

and so much harder for me to maintain someone else's scala code. It feels like statically typed perl, but that's just mho.

It is possible to write very cryptic scala code. Mine isn't, but neither was my perl.

At least with version 2.7, I actually really enjoyed looking at the Scala standard library code when the documentation wasn't sufficient for my needs. That's no knock on the scala documentation either, as I frequently look at the JDK standard library source code too. I've also had a couple of similarly positive experiences digging around the Lift code.

What other "someone else's" Scala code have you actually had to look at and maintain that you had problems with? In my experience, I only have a hard time understanding Scala code when it is doing something I'm conceptually unfamiliar with, usually something in the functional world that I know little or nothing about.

it's been a while so I don't remember the specifics but it was code for dealing with json in a lift code base.

Yea, you can make scala look like haskell, java, or perl with relative ease. Up for debate whether thats a good thing or not.

My impression of the Ruby community, especially the OSS part of it, was that they were explicitly more about tinkering than enterprise. Certainly characters like _why were in that camp.

In regards to monkey-patching, while I'm not a Ruby programmer by any means, I sorely feel its absence in other languages. It's in JavaScript (expando objects as well as being able to override and overwrite built-in methods of existing types), and to some extent available in C# (extension methods are a sort-of expando object, but there is very little ability to overwrite a method). Like operator overloading, sure, don't build a project completely off of it (I'm looking at you, C++'s iostream), but when you need it, damn you need it. I'm still delving into Python (late to the party, whatever), but I definitely use Python in much more of a tinkering fashion, so monkey-patching is something I would desire in the language.

My impression of the Ruby community, especially the OSS part of it, was that they were explicitly more about tinkering than enterprise. Certainly characters like _why were in that camp.

I don't entirely disagree with the larger sentiments, but I worry about the tone of the word 'tinker'. That is, the Ruby world is full of playful characters, and often displays a spirit that is far from corporate. However, many Ruby (and RoR) apps are also serious businesses at this point. There are also many libraries that don't charge money, do exhibit that playful or exuberant spirit, but also provide hard-core real-world functionality (e.g., Nokogiri).

tl;dr The word 'tinker' is a bit trivializing (perhaps unintentionally on Martelli's part). The division between 'tinker' and 'enterprise' doesn't fully or properly cut up the problem space. It's a binary view of a multi-faceted world.

(For whatever it's worth, this is my view as someone on the outside of this whole situation. I use Ruby and Perl about equally, primarily for systems administration and personal projects.)

I agree completely. I tend to dislike the phrase "enterprise development" anyway, because it implies that somehow our programming is suddenly different just because a company uses it. But, I try to write for my audience.

Eh. Most of my work (not all) is done within the confines of a company. There are some dramatically different ideas when you're writing something to be used WITHIN a company than when you're writing something to be used BY a company.

Most of it makes sense, or seems obvious, but your SAAS companies don't typically offer Active Directory (or LDAP) integration, as most companies aren't willing to expose their directories outside of their firewalls.

Auditing levels tend to be more paranoid, and need to be structured so that they can be fed into (or are at least sane enough to be read by) the industry-standard log analysis tools.

Admin accounts are typically transitioned to in-house users, or employees of the company, instead of being managed by the man behind the curtain (you).

So, perhaps it's that my experience is different than the majority of HN here, but to me, 'enterprise development' is a dramatically different process than what those guys at 37Signals do, for example.

C# (by .NET 4) now has real expando objects, if you really need them. It uses what they call the Dynamic Language Runtime: http://msdn.microsoft.com/en-us/library/system.dynamic.expan...

If only there was a tool to fix core objects that were tinkered with. Something like... wait. Monkey patching!

But seriously, at his Q&A session on EuRuKo 2010 Matz mentioned that monkey patching issues are going to be addressed in Ruby 2.0. http://nuclearsquid.com/writings/euruko-day2.html

As I recall, Python actually has even more detailed meta-programming abilities if you look around in it; in Python but not Ruby, a function can scan up the calling stack and determine the name a caller used for an object that the function was passed (which sounds crazy but could be nice for a DSL).

It's just that Python hasn't ever had an ethos of monkey-patching.

If you want to know about Monkey patching visit the Smalltalk community. They've been doing it a lot longer.

About point 2: the author is probably ignorant of ast module (http://docs.python.org/library/ast.html), which allows the programmer to take any ast and recompile it to anything he/she wants (in runtime, unlike Lisp macros, but alike parse tree and ruby2ruby in ruby).

This feature allows you to transform things like "1, minute" into "Minute(1)", enabling dsls, and "wrecking havoc" all over. In ruby, you don't have to do this, as there are simpler and less powerful metaprogramming tools.

So yeah. Just because you have a shotgun, it doesn't mean you can't use it to kill a fly. Of course, the really important question is not if you can, but if you should. And it is always better that this decision is left for the users of the language than to its creators.

So "the author" is Alex Martelli, a long-time Python user, contributor, and winner of the 2006 Frank Willison award which is given each year for outstanding contribution to the Python community.

I highly doubt he's "ignorant" of anything when it comes to Python...

A better theory is this article is from 2003, but the ast module was added in Python 2.6, which was released in 2008.

(I agree with everything you've said in the second and third paragraphs, but, man, try to give people a little credit before you assume they're ignorant, 'k?)

Thanks for the info. I was ignorant of the author and post date.

I seriously did not want to imply (the incorrect fact) that the author was ignorant in general, but of a single matter (which is kinda unknown, so I'd expect most be to be oblivious to it).

I am equaly sorry for the bad choice of words...

It takes efforts to write a simple Transformer using ast. Along the way of making one, your instinct will tell you that doing so is a bad idea, probably.

In Ruby, meta-programming is always powerful and always so easy to use. I don't have problem using these methods, but it pains me when some crappy monkey-patching-flavored gem (For example, cache-money) decided to bite me intermittently.

Or when debugging the code of some kick-ass-ninja-rockstar programmer where he decided to generate methods using both class_eval and instance_eval (along the way, generating the "def statements" using string interpolation) inside a module that supposed to be injected into ActiveRecord object.

I do agree with your last paragraph completely. The real ninja is capable of stopping himself from beheading anyone he sees just because he can.

Re 2): With great power comes great responsibility.

My biggest beef when I've done a little Python has been inconsistency, such as that over functions versus methods. Ruby has a simpler mental model to me. I hear this has improved in Python 3.0, but it felt bleurgh to use functions like len(str) some of the time and methods at other times (though str.__len__() is a poor workaround here). That said, the reasons have been discussed and defended numerous times, so it's definitely just my opinion rather than a critique of the language: http://mail.python.org/pipermail/python-dev/2008-January/076...

The main reason why I like having functions instead of methods is that it removes the possibility for inconsistency. In some languages, you have some objects with methods named size, some with length, etc. With python, you just have one global len method to remember.

I am often frustrated by this as well, but I'm not sure it's specific to Python or Ruby. Why is it Math.log(1) rather than 1.log()? And given whatever reason it's Math.log, why is it a.inverse() when a is a Matrix not MatrixMath.inverse(a)?

Is there a good guideline for static method vs. instance method distinction?

In python the convention tends to be:

  a.inverse() -> mutates 'a' to its inverse
  MatrixMath.inverse(a) -> returns the inverse of 'a', leaving 'a' unchanged

1 isn't a matrix. Integers/floating point numbers are in many languages treated differently than other types for reasons of performance and memory management etc. Ruby isn't different in this respect (IIRC they call it immediate objects). A matrix is a "normal" object in ruby.

BTW the ruby equivalent for the above convention would be (maybe inspired by scheme?)

    a.inverse!() -> mutate a
    a.inverse() -> return the inverse
Which one is more consistent?

I am a python user, but I _really_ like that .inverse!() notation indicating that the object itself is being mutated. It's concise and consistent. Sometimes punctuation in names is exactly what you need. Same with ? for predicates. I'll take even? over evenp any day of the week.

  1 isn't a matrix
A Fixnum could be interpreted as a 1x1 matrix. For some recursive solutions, it would probably be nice of MatrixMath.inverse accepted it as such, as well as [[1]].

This confuses me, because python strings use str.lower() to return a lowercase copy of str, leaving str unchanged. I prefer the Ruby idiom of naming the mutating versions with a !.

I agree with you, and I was equally frustrated. I think the problem is that most people don't use the Math module that much. Hence they try to keep the Numeric classes smaller rather than filling them with all possible Math functions.

However, it doesn't take much to "fix" it the way you like it. You need to patch Numeric. Whenever you call a method that doesn't exist, it calls the method "method_missing". Hence you can monkey patch Numeric#method_missing to call the equivalent Math function.

"In some languages, you have some objects with methods named size, some with length, etc."

In ruby you dont. It's #size and ruby is quite consistent in this respect. So it's remembering a method name vs remembering a function name (in a supposedly OO language).

Actually in Ruby you can often use both #size and #length.

It's part of the Ruby's philosophies to have synonyms like that.

Then why bother with OOP to begin with?

Having a global len() function is very, very linked to polymorphism. You can call it on anything that has a __len__() function defined (i.e., any class that implements something like a "HasLength" interface in Java terms).

If you have __len__() implemented on the object, why not call it directly? If you are dealing with object X, that is of classes A,B,C or D (that all implement __len__), I don't see the fundamental difference between len(X) and X.__len__().

The "magic methods" wrapped in in double-underscores are special because other parts of the language's syntax rely on them always meaning the same thing. (Some room for programmer misbehavior here.)

Example: If __len__ is defined on an object x, and __nonzero__ (Py2) or __bool__ (Py3) is not defined, then "not x" will test if x.__len__() == 0.

Similarly, __contains__ interacts with "foo in x", and __eq__ and __hash__ are expected to play well together for hashable objects (e.g. unique members of a set).

The double underscores are a useful code smell that indicates you're changing how an object interacts with Python's syntax.

I've been puzzled by len() and company for a long time and this is the most complete and sensible explanation I've heard. Thank you.

I still think it's the wrong design, mind you, but I no longer think it's a crazy one.

Tomato, tomahto.

Why use x.__len__() rather than len(x)?

There really is no difference except that len() will raise an error if no __len__ method is found on the object.

The biggest difference between Python and Ruby is in the mindset of the developers that choose them.

I think of Ruby and Python as two languages standing back-to-back in the same spot, mostly unaware of each other, greeting developers who come from different directions.

I know it's early yet, so take this with all appropriate grains of salt, but that is easily the most insightful comment I've read today, and really drives home a point I've been trying to make for awhile (and probably failed to).

I started out with Ruby, because of Rails, and just never seemed to quite make it jive with me mentally. When I found Python (because of Django), everything clicked more. I'm not even sure it was the language differences that clicked more for me, or whether it was the documentation, or the code samples, or some other intangible that made it all work.

Possibly, your greater experience was the difference. I don't know what experience you had before, but if you came to Ruby with no experience in a "dynamic" language, everything seemed new and hard-to-understand. Later, coming to Python, you probably understood everything better as you were learning it, having had experience with Ruby.

Just a guess, of course.

I'd had some experience with Perl and quite a bit of experience (had built a couple large-ish apps) in PHP.

The reason I referenced Rails and Django as motivators in finding those languages is because after having built and then forced to maintain an enterprise-class application in PHP, I REALLY liked the notion of an MVC framework, and things like Cake and Symfony weren't out (or at least I didn't know about them) yet.

So, yeah, I wasn't a programming newbie altogether, but I wasn't as comfortable in just picking up new languages either.

This is good stuff, except that he seems to misunderstand the ()-less method call in Ruby. There's no "function" or "object" being "called" with that syntax, as in Pascal or Visual Basic (his chosen analogies). There are no functions in Ruby in that sense, and callable objects are explicitly called (with a "call" method). The expression "foo" in Ruby evaluates to the value of the local variable "foo" if any, or else sends the message "foo" to self and evaluates to the result. So yes, the parentheses are optional, but the ambiguity is with local variables, not with any other form of function call--so the ambiguity is hardly noticeable.

Also, the parentheses are always optional, not just for zero-argument calls, so he also missed the opportunity to discuss "poetry mode Ruby": a bunch of method calls with no parentheses. Stylistically, "poetry mode" enables the pseudo-DSL style that Ruby frameworks use frequently, but can't really be done in Python.

Yes, there is a semantic difference here. In Python, you can do things like this:

  if baz>10:
      func = self.foo
      func = self.bar
  result = func()
Here func gets assigned something called a "bound method", which has a reference to the object and the method, which you can then apply the () operator and call. I am not a rubyist, but my understanding is that you would use a symbol to do a similar thing.

Yep, the equivalent would probably be something along the lines of:

    if baz > 10
      func = :foo
      func = :bar

    result = self.send(func)

That may accomplish the same thing, but the direct translation would be:

    if baz > 10
       func = method(:foo)
       func = method(:bar)
    result = func.call
Here, we actually deal with the Method objects, rather than just sending a dynamic message. Although the send() approach would be closer to idiomatic Ruby, I wanted to emphasize that dealing with methods as first class objects in Ruby is indeed possible.

I HATE dealing with first class functions in Ruby. I understand that it was a design choice, but man, in Python you can pass a function or a lambda and you just don't have to worry about which one you're calling. In Ruby, () will explode on a Proc object. What is with that?!!?

Because () doesn't call a method, it's simply used where a method call occurs. To call a callable object, you use #call or (as mentioned by epochwolf) #[], the latter of which I personally detest.

It's fun to contrast this with Lua, by the way.

It's the inconsistency that bothers me.

In Python, you can make anything callable by providing a __call__() method, and the parentheses will work as expected. This makes functions fungible with anything, including any object or anonymous function.

In Ruby, only methods are callable, and everything else must be called with the call() method, which means you can't use one in a place where the other might be expected. They're two concepts with the same verb--call--that require different syntax. It's inconsistent.

You can also make anything callable in Ruby: just provide a call method. The parenthesis will still work as may be expected in Ruby, but, as several folks said before: in Ruby parenthesis are not used to call anything. A method is called simply be mentioning it. Anything that follows is an argument and sometimes parameters are needed for disambiguation of the parameters. This is counterintuitive, only because it differs from most other languages. If you want to postpone evaluation, you have to wrap the method call in a closure that you .call later: in Ruby a method is not a closure and not a function pointer. It's different than in many other languages, but that alone doesn't make it inconsistent.

Thank you--this is a good explanation of why the parentheses don't work on anonymous functions: Ruby's immediate calling essentially forces you to choose between "objects that you can call like methods" and "objects that you can easily pass around." This hearkens back to carbon8's comment (http://news.ycombinator.com/item?id=1141245); viewing Ruby's handling of first-class functions as "inconsistent" definitely betrays a Python-centric view of things.

In that sense, I see why it's not so bad; the difference between methods and anonymous functions is very explicit, and when you want to pass things around, it's likely that you will have target code which expects a Proc object and source code that generates it. Perhaps it's not so bad because there are few legitimate use cases, if any, for transporting a method when an arbitrary block is expected.

Procs (lambdas, procs and Proc-wrapped blocks) and Methods are callable and all respond to #call.

As wycats pointed out in a post earlier this year (http://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-orie...), the reason you don't need to explicitly call #call on methods is that Ruby is designed for the common case of calling methods. In addition, it's uncommon to even explicitly use #call or #[] on Procs since calling them is built into the language via yield.

Lua is a great little language... I only really 'discovered' it a few weeks ago and have really enjoyed what I've seen so far. I don't think it will replace Ruby for me, but I like it's minimal feel for comparison...

Are you actually passing around methods in Ruby and, if so, why? As I've pointing out in the past (http://news.ycombinator.com/item?id=1141245), passing around methods is very rare in Ruby due to the flexibility of Procs.

Interesting! I am a half-breed; I write Python at my day job and Ruby in my personal projects. You seem to be suggesting that I am sort of making a mountain out of a molehill. Can you perhaps elaborate on this? I have definitely felt this pain before; I'm not just complaining about it because I "like the Python way more." But if I'm not using Ruby "as intended," I would like to know how I should be using it instead! I feel like the "Python way" is powerful because you don't have to think about whether you're dealing with an anonymous function or a function, whereas in Ruby you must know for sure which one you are dealing with, because they are invoked differently. I'm interested to know how it isn't really a problem, if that's how you feel.

It's not a problem in Ruby because you don't pass around method instances, you pass around Procs (wrapped blocks, lambdas and procs), so if you are passing a callable around, you know it's a Proc. If you are passing around a method instance, you should almost certainly be using a block, lambda or proc.

Also, see wycats post: http://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-orie...

If you have a method that in python would take a function as a parameter then it usually will be passed as a block in ruby.

This doesn't really work if you would want to pass several procs into a method, but honestly, that is not very common.

Well, a Method object and a Proc are two different things, but somewhat similar, so it still makes sense to discuss Proc behavior here. I would like it if this worked:

    a = lambda { |x| x + 1 }
    a(3) #=> 4
But since it doesn't, the alternatives are not too bad (though likely far too numerous for Python tastes)

    a[3] #=> 4
    a.(3) #=> 4 (on Ruby 1.9)
I think that Ruby's ability to call methods without () make for better readability in the case of writing domain specific interfaces, but come at a cost in other places. Personally I think it's a worthwhile tradeoff but I can see the other side of the argument.

You can call a proc with []

    p = Proc.new {|str| puts "Hello #{str}" }
    p["World"] # prints "Hello World"

I'm aware of this, but it's still not (). You still must be aware that you are dealing with an unbound proc object, and it must be handled differently than usual!

This discussion really makes me appreciate clojure.

Why is that?

You certainly could solve that with a symbol, I suppose, but that's a bit like doing a word-for-word translation of 'it's raining cats and dogs' into a foreign language. Better to translate to the local idiom than to try to blindly write Fortran in any language, as the saying goes.

So, for instance, you might solve it with:

  result = case
    when baz>10 then foo
    else bar
This is synthetic, of course. If the goal is to pass around methods, there are lambdas and codeblocks that can be passed around and may fit the problem more naturally than sending yourself a symbol as a message.

Or a slightly more direct translation is:

    result = if baz > 10

  result = case
    when baz>10 then foo
    else bar
is a translation of

  if baz>10:
    result = self.foo()
    result = self.bar()

  result = if baz > 10
is a translation of

  result = self.foo() if baz>10 else self.bar()
It's true that some things have to be changed when translating, but you can't ignore the deliberate structure of the original. Let's say I want to look at how inter-sentence objects work in two languages. I give you the phrase: "Raining cats and dogs. This is what the weather is like." You can't translate it as if it said "It's raining cats and dogs" without losing the basis of comparison entirely.

I like your perspective on this design decision as it seems to make clear what the intent could be. Alex reveals that the difference in Python is not so much the syntax as much as it hints at Python's treatment of functions as first class objects. This is really similar to Javascript where you can pass functions around for later evaluation. It adds a different level of complexity, of course, but it is something that I have appreciated in Python a great deal.

What struck me the most was when he spoke about Java possibly borrowing string immutability from Python and then the realization that Python is an older language than Java.

There are two things that Ruby has that Python doesn't have a good syntax for. One is blocks; in Ruby, I can write:

    foo { |x| puts x }
Whilst in Python, I need to write:

    def tmp(x): print x
It's not really very nice.

The other thing is that Python has no concept of context. In Ruby, I can type:

And it will execute foo(x) for the containing object. In Python, the closest equivalent would be:

This syntax difference may seem trivial, but it allows you to do some very interesting things to classes. For instance:

    class Foo
      def self.property(name)
        define_method(name) do

    class Bar < Foo
      property :x
Now the class Bar has a method called "x", without us needing to explicitly define it.

Ruby's instance_eval method extends this further, allowing you to execute a block of code in the context of any object you wish.

Not disagreeing with you. In fact, the first example is one reason why "print" was changed from a keyword to a function in Python 3:

  myfunc <- lambda x: (print x) or x*x  # SyntaxError

  myfunc <- lambda x: print(x) or x*x  # OK in Py3

> The other thing is that Python has no concept of context

Rather, I'd say that part of the zen of python is: implicit is better than explicit, and I prefer python's behavior in this instance.

You've got that backwards for Python: Explicit is better than implicit.


This is a fairly superficial difference, but Python's syntax grammar is very conventional, Ruby's isn't. Compare the grammars of Python, Ruby, and ANSI C.

Python: http://docs.python.org/release/2.5.2/ref/grammar.txt

Ruby: http://web.njit.edu/all_topics/Prog_Lang_Docs/html/ruby/yacc...

ANSI C: http://www.lysator.liu.se/c/ANSI-C-grammar-y.html

Python and ANSI C both have a similar organization of productions and names for statements and expressions. It's pretty easy to follow the grammar to obvious points where the language differs. What are valid unary expressions in Python and ANSI C? There's a production in each grammar that makes a convenient starting point and comparison is easy. What is a valid unary expression in Ruby? It's much less clear.

The Ruby grammar isn't large, so I'm sure some people prefer the way it's organized. Once you've absorbed the whole thing questions like "what's a valid unary expression?" don't matter anymore. But I think it's an interesting distinction to someone trying to decide which language to learn.

I cringed when I saw this link, because I was a regular on comp.lang.python at the time. I remember very clearly this discussion and the ensuing flame war. This was when it dawned on me that these types of discussions live forever on the internet. It pays to tone down your rhetoric, especially if you use your real name in the discussion. <sigh>, at least the link was not to the flame war in general.

It's to be noted that while Ruby CAN change builtin functions, the metaprogramming ability is much more often used in cases like ActiveRecord, where you can say `acts_as_list`, and it reopens the AR class and inserts new list-related functionality. It's more powerful than simply using modules, since it can make decisions as it's being included.

Working with Ruby on a day-to-day-basis, I have never reopened core classes and changed anything. Rails comes with some common extensions which simply add new methods, which are less error prone (although I avoid Rails & ActiveSupport whenever possible).

I think he's totally right about how to call methods. They just don't feel as first class as they do in JS or Lisp. I don't agree with his criticism of dynamic strings, however. The only time you want immutable strings is when you're not using them as strings. Ruby has symbols for when you need a hash key.

Ruby and Python are so similar, in fact, that I'm really surprised that there is no ongoing effort to merge them together. They could really use one another's libraries and various VM implementations, for example; even the parsers are almost identical.

The only things that are really "different" between Python and Ruby are topical features of the grammars of each, that mostly disappear once the code has been AST-transformed. These could likely be expressed more succinctly in terms of a simple common base grammar, and two small, modular sets of reader macros.

As somebody who used Python a lot and being unable to learn Ruby, I have to say that I don't feel they can be merged.

I have been successful at learning a variety of languages. Ruby was difficult for me because it is so similar to Python and yet so different. You have almost the same syntax getting very different results. So if you come from Python you get the exact opposite experience to the principle of least surprise. On the other hand, if you move from OCaml or Erlang there are no surprises because the syntax is different enough that there are no pre-expectations.

Also, metaprogramming in Python and Ruby are quite distinct. The way Python went about it using metaclasses and decorators is very different to the monkeypatching you get in Ruby, so I don't expect the AST results to be that similar for idiomatic code in both languages.

Basically Ruby and Python fill the same niche.

When two species fill a different niche in nature, they can co-exists. When they fill the same niche, it is a bitter fight for survival.

more evidence: vi vs The Unholy, er, I mean, Emacs

One of those is an editor. The other is a lisp environment with editing functions.

There are many small-to-medium differences between Python and Ruby, but in my experiences, no one actually chooses one language over the other because of these differences. When you ask someone, "Why do you prefer Ruby over Python?" or vice-versa, everyone has their favorite differences to talk about, but often, the choice is actually made before enough familiarity with both languages is established to be aware of those differences. Most people choose between the two based on je ne sais quoi, rather than an objective comparison of the differences.

More succinctly: "Ugh! Python? I hate the idea of syntactic whitespace," and "Ugh! Ruby? I have the idea of using @ for instance variables," are both cursory judgments that mask deeper preferential foundations therein.

I love the cooking analogy in the middle I use them often when talking about programming!

For me, the biggest difference between Ruby and Python is that Ruby is more perlish (i.e. more hacker, one liners, clever stuff) while Python is cleaner but a bit more verbose and less clever. So in that sens, when I want to hack something, I usually go with Ruby.. but for a big project, I'd prefer Python.

(But these days, I'm mostly hacking with Arc :p)

Why was a seven year old post resurrected for discussion here? Does anyone still believe that Ruby, even with its unbridled dynamicity, is just for tinkerers and won't work for large teams or production quality applications?

Very interesting to read. And it raised the question in my head about immutable types. I like functional languages a lot and I like it if just everything is immutable. Also, I like C++ where I just have everything mutable. But to have both immutable types and mutables types in one language always felt very inconsistent to me. Are there good reasons for this?

I put this question also on Stackoverflow: http://stackoverflow.com/questions/3584945/non-technical-ben...

I just get a login screen.

+ Newspapers require me to register and login - I go somewhere else.

+ Commercial sites require me to register and login - I go somewhere else.

+ Google requires me to register and login, guess what happens.

I can't be bothered. I've got stuff to write, products to ship, and a life to live. It's just another article. I've saved time by not reading it, and the chances it was useful are small. Besides, skimming the comments has given me enough of the flavor that I don't really think I'm missing much that I didn't get from the comments themselves.

Score another one for the HN community. Thank you.

This seems to be a bug in Google Groups -- if you have an expired session, it forces you to log in. But you were never signed in (or if you signed out) it doesn't ask and goes straight to the article.

It's actually a Usenet post on comp.lang.python. You sound old-skool enough to still have access to a NNTP server ;-)

That's an easy one... In Ruby, I can do string.reverse(). Python makes me type string[::-1] to reverse a string. In a nutshell, that is why Ruby is better than Python.

I must admit that I use Python more than Ruby. I like both, but know Python better. I feel Ruby is more consistent though. Either is a good language!

You can actually say `reversed(iterable)`, which returns another iterable yielding everything backwards. Now, this is not returning a string, which leads to some warts:

* list(reversed(a_list)) * "".join(reversed(a_str))

and the like.

One thing that bothered me in Python is lambda expression being limited to one line (as far as I know). Procs/lambdas in ruby can be very complex which often need.

But if I wrong about this, please let me know.

"Other syntax differences such as '@foo' versus 'self.foo', or the higher significance of case in Ruby vs Python"

...suddenly wonders if there's a language where both case and indentation are significant.

In Haskell, indentation is significant (Python-style), and case is used to separate values from various special names.

values, keywords: all lowercase modules, classes, types, constructors: initial capital

For example, it's impossible to declare a class named "string", or a value named "String".

I think he was talking about the "case" control structure. Or maybe I should just go to sleep... In any case, "case" returns a value, doesn't need breaks and doesn't need to be the same type. That's awesome.

Given that the switch-case construct doesn't exist at all in Python, I'm pretty sure he was talking about character case.

Haskell I believe

Surprised nobody has mentioned this: Python's generator expressions and list comprehensions are delicious. I'm primarily a Ruby guy, but I think Python could teach us a thing or two. :)

make sure to use the zoom button on that... (not only the page itself but the differences too)

Try Readability (http://lab.arc90.com/experiments/readability) - it will fix the monospace and excess whitepace (as well as the size) in a single click.

Fun comparison of Ruby and Python. The pasta analogy was especially entertaining.

One good reason: programming style (AKA personal preference). Python is a fine language. Ruby works better for me -- but that's just me.

This is one of those articles where any discussion thread about it will lead to the sharp knives coming out between the Pythonistas and the Rubyists. The languages are so close in so many ways, and yet so different, that it is bound to happen.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact