Chad Perrin: SOB

14 March 2006

A Serpent in Ruby’s Garden

Filed under: Geek — apotheon @ 02:33

Discussion with a Pythonista friend of mine has gained some unexpected depth in the recent entry here, The Problem With Python. Some of that, I think, warrants being addressed on its own here.

As one might gather from some of my earlier commentary both here and elsewhere, the programming language Ruby suits all of my needs (as far as I’m aware) that Python might have addressed. Between Perl and Ruby, I simply don’t have any particular need for what Python offers. This doesn’t by any stretch mean that Python doesn’t have its benefits, however, and I may end up coming back to learn it at some point in the future anyway — if only to gain a little more perspective on programming in general, and on the other languages with which I’m familiar.

Coming from the direction of greater familiarity with Perl than any other language, I tend to see Ruby as something I want to learn, something to pursue, something of great interest, and to see Python as something of an interloper in Ruby’s realm. Ruby better suits my needs, and thus takes the place as my default choice for a more elegant, more object-oriented language, with a form perhaps more aesthetically pleasing.

Thus, my personal biases make Python the intruder, despite the fact it is Ruby’s elder, and that it is more established and widely used at least here in the West.

In particular:

  • Alternative to Perl: Python has gained much of its user base from the Perl community, as defectors sought a programming language that doesn’t suffer Perl’s less aesthetically pleasing characteristics, and who wanted greater ease of certain programming language characteristics that Python provided. Those moving from Perl to other languages, however, came to Perl for a reason, and in many cases Python provides an imperfect solution for such people because in addition to discarding what they see as negatives in Perl, Python also discards some positives. As some of the following bullet-points will indicate, it’s my estimation that Ruby does a better job of preserving Perl’s positives while improving upon its negatives.

  • Instructive Suitability: One of Python’s greatest, but perhaps insufficiently emphasized, contributions to the general realm of programming languages is its suitability as an instructional tool. It has the benefits that Pascal has enjoyed as a didactic tool, teaching many good habits by enforcing them strictly within the very syntax of the language. Python improves upon Pascal’s example by having a more dynamic syntax, however, and being more recognizable in its appearance as having cause-and-effect terminology translatable to plain English.

    While Ruby doesn’t strictly enforce good habits the way Python does, it strongly encourages them, and allows for a little more leeway in providing a glimpse of what’s possible outside the realm of traditionally “good” habits as well. I, for one, consider that a positive in an instructional language, though some others might not. On the other hand, Python’s ease of reading based on its translatability to plain English is a characteristic Ruby shares in at least equal quantity, from what I’ve seen. I tend to avoid looking at Python source in general, but I think I’ve probably seen enough to make a rough comparison, and in this matter Ruby seems to come out on top. Maybe that’s just me, though.

    The suitability of Ruby to instructional purposes is well supported by the proliferation of often quite excellent beginning programming instructional texts that use Ruby as their example language. In particular, there’s the Pragmatic Programmers’ Programming Ruby, also available in an older edition for free online, Chris Pine’s Learn to Program which is available as both an online book and in an “improved expanded version” as a hardcopy book published under the Pragmatic Programmer’s imprimatur, and the quirky and utterly entertaining Why’s (Poignant) Guide to Ruby by a man who calls himself “Why the Lucky Stiff”, available in electronic formats online. I’ve never seen this weight and volume of instructional materials presented with such friendliness while aimed so clearly at non-programmers. The above examples are simply the best-known: other introductory texts are numerous and widely available. While this doesn’t prove Ruby is better suited to beginning programming instruction than Python, it at least seems to provide an awful lot of attractiveness of the language (and, perhaps, its community) as a didactic tool.

  • Object Orientation: One of the better implementations of object oriented programming in a language available today is Python’s. That’s something I don’t dispute, of course. What little I know of it directly indicates that it’s quite slick and well-implemented. It does not suffer the common failing of OOP characteristics in many other languages, appearing to be tacked-on or bolted-on as it does in Perl and C++, or even worse like PHP’s or Visual Basic’s OOP implementations. Even Java’s OOP characteristics are cumbersome to use in many ways that Python’s aren’t, making it a considerably less elegant solution to the problem of wanting object oriented programming to make one’s programming endeavors work out better for the inclusion of such techniques.

    Both OOP and procedural/functional programming characteristics are built into Python. It’s nearly impossible to write useful Python code without using both methods and functions, and combined with other non-object elements of Python, you end up with three or four different types of syntactic elements that make regular appearances in Python code. It thus falls short of the Smalltalk ideal of object-orientedness, sharing a space with languages like Objective C — languages that elegantly but incompletely incorporate OOP characteristics in an impure OOP environment. It has long seemed unlikely that a “real world” workhorse-worthy language could be as purely object oriented as Smalltalk, which rests in obscurity in large part because of its incredibly “pure” implementation of OOP principles, but Ruby is turning that perception around.

    Ruby takes object oriented programming to Smalltalk’s level without giving up general-purpose usefulness, even for tasks that are generally better suited to a procedural and/or functional programming style. Object orientation is so subtly and pervasively infused into the language that one can apply its syntax in a procedural style and effectively ignore the fact that the procedural structure of your code is composed of nothing but objects and methods. Even so-called “operators” are merely methods in Ruby, but you wouldn’t know it at first glance, and as such it is easy to ignore that characteristic of the language when it is convenient to pretend it’s procedural in nature — and yet, because of this very fact, the commonality of methods in the general syntax of the language lends itself to counterfeiting functional programming techniques as well when it is convenient to do so. Ruby has managed to not only provide a pure, self-consistent object oriented programming environment that Python hasn’t, but has also done so in such an elegant fashion that it can be leveraged to approximate non-OOP environments as well (though of course it does not provide such capability as fully and well as truly procedural and functional languages can).

  • Readability: One of Python’s great strengths arises from a characteristic that I also see as a weakness. It’s funny how things balance out that way. Because of enforced rules of formatting which limit the programmer’s ability to fit code to his needs, it also becomes more cleanly formatted by default than the source code of languages like Perl and C/C++ which do not have such rules. Style and syntax become intertwined, almost inseparable concepts in Python.

    While my tendency is to refer to this (sometimes referred to in a very general sense as The Whitespace Problem, though it does go beyond significant whitespace) in a general sense with reference to the programmer’s options for individual needs, I do not mean that in a merely stylistic sense. As scoth pointed out, the problem goes beyond prohibiting merely stylistic preferences: it also involves greater issues of discouraging complex logic at times because of a desire to avoid too-complex indentation handling. The flipside of this, as he also pointed out, is that it encourages prefactoring your code to create simpler, probably more elegant, solutions to the same problems.

    All things considered, Python’s metastylistic rigidity is quite valuable as an alternative to Perl’s complete lack of endemic stylistic guidance. Where Perl syntax is unstructured enough by nature to allow sprawling, unreadable gunk that looks like Snoopy swearing at least as easily as it allows well-formed source code that is vaguely readable later, Python strictly enforces neat and orderly rows of code. Of course, you can produce unreadable code in Python just as with any other language, but it takes real effort to achieve the same lack of readability as one can get without even being aware of it with Perl.

    Adding Ruby to the mix seems to provide the needed middle ground. It easily allows basically any stylistic preferences that Perl does, including the six hundred column obfuscated one-line programs that Perl would allow to anyone insane enough to want them. On the other hand, its syntax, semantic structure, and community in general discourage such practices. One needn’t use any indentation at all in Ruby, a language that like Perl, C/C++, and most others does not use significant whitespace, and yet its end-statement syntax makes sloppy or nonexistent indentation generally unpalatable in a way that Perl does not.

    Another shortfall in this area is that, combined with the enforced indentation rules of the language, Python does not have a “case” or “switch” statement (something even PHP, with its otherwise woefully anemic syntax, does provide). Nor does Perl, strictly speaking. Combining these two “features” in Python, however, does produce some particularly annoying discouragement of complex, yet concise, algorithm implementations, as scoth points out. It’s true that one can construct a reasonable facsimile of a case statement in Perl in one of any number of ways (there’s TIMTOWTDI again), and I can only assume that it wouldn’t be terribly difficult to implement at least one such approximation in Python without resorting to ugly if/elif/else tangles, there’s just no substitute for a simple and preconstructed case statement built into the language’s syntax. Of the three highly functional dynamic high-level languages in the Perl/Python/Ruby family, only Ruby provides a case conditional. Hallelujah. Can I get an amen?

    It’s sometimes reported that even nonprogrammers can often muddle through some simple program logic in Python and get the gist of what’s going on. This is pointed to, in contrast to Perl’s tendencies, as evidence of Python’s increased readability — and with good cause. The same is said of Ruby code, however. The fact the same has been said of COBOL doesn’t excuse its obtuseness, of course, but neither Python nor Ruby suffer that same inelegant nastiness, and they get much of their readability from well-formed syntax and semantic structure rather than from absurdly formed syntax torturously shoehorned into atomic similarity to English without concern for how the resultant program logic is going to look. Both Python and Ruby use clear, concise syntax and well-conceived semantic elements, as well as enforced (Python) or encouraged (Ruby) stylistic characteristics, to create a welcoming, understandable environment for the programmer who must make sense of already written code.

    . . . except that Python’s enforced stylistic characteristics “make my eyes bleed”. Looking at Python code for too long can literally begin to induce a headache for me. I think a significant part of the reason for that is its asymmetrical appearance: blocks of code have enforced indentation with no clear indicator of the end of a block, drawing my eyes further toward the right-hand side of the screen, creating a sense of cognitive and aesthetic dissonance. I get lost at least as easily in well-formed Python code as in poorly-formed Perl code (as opposed to well-formed Perl code which, despite the claims of its detractors, does indeed exist). Ruby creates no such problems for me. I doubt it is limited to the simple fact of Ruby’s use of the end keyword to close a block of code, but that’s the only obvious difference that comes to mind when I consider the matter. It also provides sort of an arcing form to the left-side margin of the code that makes it easier to track constructs in the code without having to use four or more columns of indentation, at least for me, so that the Ruby community’s preference for two-column indentations perfectly suit readability tendencies (while programming communities like Perl’s tend to favor four-column indentation for purposes of readability).

  • The Wheel Has Been Invented: Python does not yet compare to Perl’s CPAN in terms of the existence of pre-written code to do Everything Under The Sun, but it does have a strong, and growing, repository of useful code that can help one avoid having to reinvent the wheel. The ease of tying in C/C++ libraries in the use of Python is likewise helpful in this regard, and reportedly somewhat smoother than such operations can be in Perl at least some of the time. While Ruby doesn’t have the sheer volume of available code that even Python, let alone Perl, has, and I am not aware of how easily code from other languages can be tied into Ruby (yet), it does have advantages in this area.

    In particular, Ruby’s dynamism and flexibility as a language encourages the reinvention of other language’s wheels, but instead of merely reproducing the same algorithms by simple replication using a different syntax, Rubyists tend to enjoy finding new ways to do the same old things in the process of translation. In fact, enjoyment is one of the major selling points of Ruby, and is indicated by Matz (the language’s creator) to be one of the primary reasons for its creation: he wanted a language that was thoroughly fun to use. As a result, though it lags in volume of extant reusable code, the current growth rate of such code in circulation is stunning to contemplate.

    In addition, Ruby seems somehow to encourage the creation of leverage almost as much as the creation of code. Frameworks and other tools for the creation and reuse of code are becoming ever-more prevalent in the Ruby world, and tend to have incredibly attractive interfaces. The Ruby Gems system combines the comprehensive usefulness of CPAN, for instance, with the elegant simplicity of Debian’s APT system, improving on the diadem that rests on Perl’s brow in terms of functionality and ease of use even if its contents are not nearly as extensive. Rails, the “killer app” Ruby produced that is taking web application development by storm, leverages Ruby’s metaprogramming capabilities to provide a development framework that makes web development as much a joy to accomplish as anything else in the Ruby world — more so, perhaps, for those of us who enjoy web development for its own sake. One-click installers and the like for Ruby and Ruby-related tools such as Rails and Gems (and other, less famous, tools) are cropping up all over the place and make the sometimes tedious and painful task of installing Perl and Python on operating systems other than free unices seem outdated and unnecessarily troublesome. In addition to this, I’ve never seen an interactive programming shell that rocked my socks as much as Ruby’s irb. In fact, irb has become my calculator: it’s easier to use than any other shell’s tools, or even any command-line or GUI calculator applications, I’ve ever encountered, as a general-purpose calculator.

    On the other hand, there’s also a lack of maturity to some of the resources available to Ruby, where the same lack of maturity does not exist in the Python world. Python is definitely a more well established language, at least in the West, and many of its tools are as stable as one might generally desire. Ruby is only now beginning to be offered for easy access in many Linux distributions’ software archives, for instance, and thus the quirks are still being ironed out in some cases, while Python is actually so well integrated that it’s necessary to the operation of some installers and software management systems (think the Anaconda installer for Fedora Core and emerge for Gentoo, for two examples). Hopefully, this gap will close quickly, but in the meantime Ruby is quite readily available on the distro of my choice (Debian) and is still loads of fun to use, where Python (to beat a dead horse, perhaps) “makes my eyes bleed”.

Commentary is, as always, welcome.

8 Comments

  1. I have been using perl for the last 10 years, and doing occasional bash/csh scripting for about the same period of time. So I feel like I have a few things to add to the ruby v. python debate. I’m a big fan of both languages, largely because of how much better they are than what came before.

    From a practical perspective, ruby sometimes falls short. For example, event-driven network programming. There is no equivalent to twisted in ruby, however much I wish there were. That means that not only is the code to do equivalent functionality in ruby an order of magnitude longer and more complex, but it is also going to be inferior in both performance and correctness (threading, deadlock, whatnot). If you are planning on writing any sort of decent network application, the choice between ruby and python is clear: choose python. Until ruby has something equivalent to twisted’s most basic reactor, this will be true. (those of you who have never experience the bliss of working with C’s libasync, python’s twisted, or other equivalents might be scratching your head and thinking, “of course ruby can do that.” I assure you, it cannot — at least not until someone writes a twisted-equivalent for it).

    OTOH, ruby is often ‘cleaner’ than python, this is true. The language has more of a ‘well-thought-out-from-the-beginning’ feel, than the somewhat organic, PEP-added feel of python.

    Again, getting back to practicalities, python is simpler. It is very easy, in python, to do introspection/reflection on any object by just typing “dir(obj)”. In ruby, there are a handful of methods you have to call to do the same thing (instance_methods, etc). The ruby way is much more precise. The python way is much simpler and faster. Because of this simplicity, python programmers often tout it for speeding up their development cycle. I find this to be true — I can bang out more functioning code in 30 minutes in python than I can in a full 60 minutes in any other language I’ve encountered, including ruby (compare it to c/c++, and I’d say its more like 120 minutes). This isn’t a result of familiarity — I’ve logged *many* more hours in Java and C++ than I ever have in python, and my total experience in python (over the past 2 years) is just now catching up with my perl experience.

    This is starting to sound like I’m coming down hard in favor of python over ruby — really I’m just trying to balance the discussion — but one other thing I’ve noticed about python that perl/ruby lack: extreme readability. I can come back to python code I wrote months ago and immediately know what its doing by just glancing at it. Perl is notorious for not having this property. Ruby, sadly, seems to have inherited this characteristic from perl.

    Comment by henry — 15 March 2006 @ 09:32

  2. I think you’re the only person I’ve ever run across who thinks Ruby is less readable than Python. Not even serious Python hackers have expressed that opinion, in my experience.

    Comment by apotheon — 15 March 2006 @ 10:21

  3. I know what you mean about Ruby being fun. I’ve been driven to rewrite old algorithms in Ruby just for the fun of doing them better.

    “Programming Ruby” has a chapter “Extending Ruby” that describes how to use C to extend Ruby or bridge it to other languages.

    Comment by Sterling Camden — 15 March 2006 @ 11:29

  4. While I’m a huge fan of why’s poignant guide (honestly, that’s got to be one of the top 5 programming books, EVER), and while said guide makes a lot of the readability of ruby, I don’t quite agree. There is a lot of clutter in ruby. It’s leaps and bounds ahead of perl, absolutely. But it simply isn’t as readable as python. Might that be a subjective evaluation? Sure. But I’m not alone in making it. In my experience, the only ones claiming that ruby is more readable than python are those who aren’t very familiar with python (and unfamiliarity is equivalent to unreadability, from a completely subjective standpoint), or who only have perl to compare it against.

    Comment by henry — 15 March 2006 @ 01:00

  5. From what I’ve heard, from Rubyists and Pythonistas of every stripe, seems to be that Ruby and Python are roughly equivalent in readability. From Perl hackers, I hear that Ruby is more readable than Python. Even the Pythonistas I’ve talked to tend to consider Ruby’s syntax as readable as Python’s, if not more so, with the exception of indentation issues — which they tend to think evens out the readability.

    Of course, from my perspective, the indentation requirements of Python tend to make it less readable rather than more, as long as some kind of reasonable style is applied in code in other languages that don’t have the same significant whitespace requirements. Then again, I’m far more a Perl hacker than a Pythonista.

    I’d like to get scoth’s take on readability of Ruby vs. Python, if he’s paying attention.

    Comment by apotheon — 15 March 2006 @ 01:11

  6. Acutally, I have never written a line of Ruby and before I just went and looked up bunch of examples I haven’t even ever seen much Ruby, I mostly only know it by reputation. After looking at a more examples, all I can say is that I’m decidedly undecided and I can’t intelligently give an opinion on which is more readable ;-)

    Most of the examples of Ruby that I saw looked pretty readable, even fairly complicated ones. It does have more special characters and syntactic structures which would make it more difficult for a non-programmer to read, but it seems to have a pleasing aesthetic and looks fairly intuitive to anyone who already has some experience with either Perl or Python.

    By the way, I very much enjoyed reading this article. I think it gives a good comparison of Python and Ruby with just a touch of Perl thrown in. Your bias is still evident, but it’s unobtrusive and not unfair. I also like that it’s inspired some insightful comments from people who didn’t seem offended by your position :-)

    Comment by scoth — 15 March 2006 @ 05:56

  7. Hi.

    I’ll be playing the role of the bitter python zealot today. Tips are appreciated after the show.

    Really, I thought your article had some pretty good information in it, but I found myself wanting more details. I’m weird that way… I generally prefer more uncooked data than conclusions, so I can figure out whether I agree.

    When I see claims with no supporting details, I’m left wondering “Where’s the beef?” So I must apologize in advance for wanting more beef.

    Anyway, the article raises a bunch of interesting issues. Sorry for the length of my reply. Bitching makes me clarify my gripes, and because of that, helps me learn. :)

    Article, in general…

    I was a little confused about the purpose of the article. I couldn’t quite tell if it was a comparison of the differences between python and ruby, or if it was primarily praise for ruby with some added bits about python and perl. It seemed to drift back and forth.

    Perl

    For someone coming from a perl background, I would expect Ruby is easier to learn than python, and makes more sense. It’s a change from perl, but not a huge leap… sort of a Perl++, if that doesn’t understate the differences too much. That doesn’t make the language innately better or worse, though… just more friendly to perl-minded people.

    Object orientation…

    I keep hearing that Ruby is more object-oriented than Python. But I see no evidence thus far to support the claim, and a small amount of evidence against it. Could you try to explain what I’m missing?

    The definition of “object oriented” is a bit vague, which makes the topic somewhat hard to discuss, but I’ve found a somewhat decent definition: http://www.paulgraham.com/reesoo.html

    The list seems to be missing something, though… I’d add Syntactic polymorphism, meaning that any entity in the language can be manipulated by using a consistent syntax, regardless of the type of entity. This is one particular area I find Ruby lacking. It’s not as bad as perl in this respect, but it’s still not great.

    Number 6 on that list is one area where Ruby’s policies seem more OO than Python; Ruby forces the use of getter/setter methods to access class members, while Python leaves it up to the programmer to decide how the data should be accessed. You can implement number 6 in Python, but the default is not to. I haven’t learned enough about Ruby yet to know if its default policy can be changed too, allowing you to do away with all those getter/setter methods, or if this is an instance where ruby enforces a particular style.

    Also, I know I’ve mentioned it before, but it pisses me off that you can’t pass Ruby functions/methods around as parameters to other methods without specially transforming them first into passable objects. I know Ruby methods are supposedly objects, but what good does that do me if I can’t treat them like other objects?

    I also dislike the punctuation prefixes on symbols, and other restraints on the symbol names. If I change something from a global variable to a local one, or from a constant to a variable, why should I have to rename every reference to it? Shouldn’t the code figure that out automatically from context? It’s not like the symbol names are ambiguous… unless someone is using “foo”, “Foo”, “$foo”, “@foo”, and “@@foo” in one program, in which case they need to be beaten with a blunt object.

    Anyway, back to object orientedness…

    One thing to note, perhaps, is that Python does not inflict an OO approach. So in this sense it may be considered less object-oriented than some languages, because it doesn’t care whether you use a functional style, imperative, object-oriented, aspect-oriented, or even declarative. The OO definition above mentions something about this too… C/C++ folks see OO as a liberation from a world that has nothing resembling first-class functions, while Lisp folks see OO as a prison since it limits their use of functions/objects to a particular style. Being “more object-oriented” isn’t necessarily a good thing — it depends on what you want to do.

    Lambdas / blocks / anonymous functions

    One of my biggest problems with python is the lack of anonymous chunks of code. Its lambdas are useful, but extremely limited. This is one of the things you’ve mentioned too.

    However, I’m not much happier with Ruby’s alternative. It’s not limiting in the same way as Python’s lambda, but it’s convoluted and messy. There are two incompatible ways (method vs proc) to define a chunk of code, and I haven’t been able to find any good reason for the complexity caused by having both. Ruby is a significant step in the wrong direction, even from perl, in this regard.

    I can’t say much for python here either. It has basically no support for inline or anonymous code, because Guido doesn’t like it. This is an annoying exception to python’s usual policy-over-mechanism rule.

    In perl, the syntax for defining and calling a regular function is exactly the same as for an anonymous, inline lambda-type function. Apart from the funky punctuation, it’s a pretty simple matter to pass code around, and quite powerful.

    But in Ruby you must decide whether you want the relatively rich function signatures and parameter handling of methods, or the convenient inline-able definition syntax of Procs. Methods, as far as I can tell, cannot actually be passed as parameters. So, the receiving function doesn’t have to care which type it’s receiving (since only Procs are passable), but it does have to restrict the way in which the caller passes the code.

    The implicit block syntax may be convenient now and then, but the complexity it causes doesn’t seem (to me) worth the minor convenience. The entire concept could be tossed out of the language, without significant detrimental effects. It just means the caller would have to add one word and put a paren in a different spot, and the receiver would either A) lose a “&” symbol, or B) need to name its formerly-implicit parameter.

    in addition to discarding what they see as negatives in Perl, Python also discards some positives.

    Could you list the specific Perl benefits, other than anonymous inline function definitions, which Python discards?

    Python as a teaching tool…

    It’s often stated that Python is a good first programming language. But why is this? Could it be, in part, that its approach is a good match for how humans think? If so, isn’t that a pretty significant benefit? Or perhaps that it does a good job of avoiding unnecessary complexities (yet without tossing the baby out with the bath water, like PHP does)?

    Making something complex is easy. Making it simple is the hard part. And it’s awfully easy to ignore the value of simplicity, because good design looks easy. “I could have done that” is a common response by people who, in fact, did not do it and probably never would.

    You didn’t say “I could have done that”, of course. I’m just waxing philosophical tangents. :)

    The suitability of Ruby to instructional purposes is well supported by the proliferation of often quite excellent beginning programming instructional texts that use Ruby as their example language. … I’ve never seen this weight and volume of instructional materials presented with such friendliness while aimed so clearly at non-programmers.

    Take a look around Barnes & Noble. Or, for that matter, take a look around the web. Look for some Java books, and you’ll most likely see much greater “weight and volume”, both for newb-level books and advanced topics. But does that make it a better language, or easier to learn? No, it just means the language is more popular.

    However, I think you very deeply for the book links. I’ve been having a particularly hard time researching Ruby in order to compare it fairly, because I can’t find proper documentation for it. I’ve been looking for a decent source of Ruby documentation for a while… It must be A) free, B) online, C) up to date, and it must D) include a fairly complete reference of the language, its bundled modules/libraries, and its APIs for interfacing with other languages. But I have found nothing suitable. As far as I can tell, there is no up-to-date online Ruby reference. The best I’ve found is the rather small, outdated, and incomplete “official” ruby book. If my findings are correct, this is a serious problem, and a few 3rd-party books aren’t going to solve it. Some books which are “quirky and entertaining” are no substitute for “accurate and complete”.

    Compare, for example, to http://python.org/doc/ . It may not be very entertaining, but it’s accurate, complete, well-organized, and it can tell you pretty much anything you want to know about the language. And, as an added bonus, a huge chunk of the material there is available on your computer, from inside python itself. Just type “help(something)” from inside the python interpreter, where “something” is the object you want information about. (or run “pydoc foo” where foo is a module name or source file)

    … teaching many good habits by enforcing them strictly within the very syntax of the language.

    Which good habits, other than indentation, does Python enforce?

    Also, can you list an example of when you would intentionally want not to use good indentation? (obfuscation contests don’t count)

    I’d also like to know about other instances where python forces the programmer to do things in a certain way. I’ve mostly only seen it do the opposite, focusing merely on mechanism and letting the user use whatever policies they like.

    Maybe I’ve just had too much of the “python kool-aid” to see the cage it puts me in… or maybe you’re imagining bars where there are none.

    BTW, have you noticed any of the style-related things Ruby enforces? For example, ever seen it dump this error? “warning: don’t put space before argument parentheses”

    It’s nearly impossible to write useful Python code without using both methods and functions, and combined with other non-object elements of Python, you end up with three or four different types of syntactic elements that make regular appearances in Python code.

    Can you define the difference between a method, a function, and a procedure? Can you provide an example where python distinguishes between them in a significant manner? Like, where it actually matters which is which?

    (Basically, a function is a procedure which returns a value, and in some definitions, has no side effects. A method is a function that `belongs’ to an object and is named obj.methodname, where obj is some object (this may be an expression), and methodname is the name of a method that is defined by the object’s type. In practice, the only difference is whether you type “dostuff” or “obj.dostuff”, which as far as I can tell is handled the same way in Ruby as in Python.)

    I’d assert that it’s nearly impossible to write useful Ruby code without using both Methods and Procs, each of which require special treatment, but I griped about this earlier.

    What, specifically, have you found to be “non-object elements” in python? Which properties of an object are these elements lacking? I ask, because I can’t think of any non-object elements in Python.

    Object orientation is so subtly and pervasively infused into the language that one can apply its syntax in a procedural style and effectively ignore the fact that the procedural structure of your code is composed of nothing but objects and methods. Even so-called “operators” are merely methods in Ruby, but you wouldn’t know it at first glance, and as such it is easy to ignore that characteristic of the language when it is convenient to pretend it’s procedural in nature — and yet, because of this very fact, the commonality of methods in the general syntax of the language lends itself to counterfeiting functional programming techniques as well when it is convenient to do so.

    That paragraph is entirely true, even if you substitute “Python” for “Ruby”. What confuses me is that I thought you were trying to explain how the languages differ, not how they are the same.

    Style and syntax become intertwined, almost inseparable concepts in Python.

    Other than indentation (which still has style issues of its own, like how much to indent, where to break long lines, how much to indent continuations, whether to use tabs or spaces, etc), can you name any aspect of Python where the syntax requires a particular style? The only one I can think of is the inability to define more than one function on a single line without using lambdas.

    There are definitely strongly-encouraged policies, but those are not the strict mechanisms you seem to be describing. For example, nearly all python code uses “self” for a method to refer to its instance object, but you could call it “this” or even “bob” if you wanted to.

    What indentation-handling issues did Scoth mention? I may have simply not noticed them, since vim takes care of a lot of things for me. For example, I can’t nest/unnest blocks in python by adding {}’s, but I can indent/dedent blocks in vim with a couple keystrokes. It would be kind of a pain if my editor didn’t support it, though.

    Python does not have a “case” or “switch” statement

    True.

    At first, I missed switch/case. Well, except for its fall-through case properties, which can get rather ugly. Take a look at Duff’s Device, for an example. I really just missed having a convenient way to do a bunch of if..elseif..else clauses.

    But after a rather short while, I forgot about switch/case completely, and haven’t missed it. Why? I found better ways to write that sort of code. The presence of a switch/case almost always indicates that you’re making code too “smart”, and need to convert the logic into data to be handled by relatively “dumb” code. This is rule #9 in The Art of Unix Programming, the Rule of Representation. “Fold knowledge into data, so program logic can be stupid and robust.”

    http://www.faqs.org/docs/artu/ch01s06.html

    I’m not saying a switch/case is never useful… Duff’s Device is a good example of both the good and the bad aspects of it. But most of the time, it’s not a very good way to solve problems.

    Readability …

    Most projects big enough to matter are going to spend most of their lives being maintained rather than created. The code will get read many more times than it is written, and by many more people. This seems to be widely agreed upon, though. Readability is important.

    What I wonder is why you consider Ruby more readable than Python. I haven’t found it to be, but perhaps I’ve been looking at the wrong code. Do you have any examples of Ruby programs which are easy to read?

    Er, I was looking around just now for ruby examples, and the first hit on google brought me to a site with some little example programs. Here’s one of them: result = nil IO.foreach(ARGV[0]) {|x| rand($.) < 1 && result = x } puts result Can you tell me what this does, and how it works? Do you think a newb programmer could tell me?

    For that matter, could a programmer experienced in other languages (but not ruby) tell me what it does? It appears to open its own source file, iterate over each line of itself, assign a result value every time a random number is less than 1 (which in most languages will be true either 0.00000000% or 99.9999999% of the time, depending on whether rand() returns an int or a float), and then print out the last value. This would, effectively, print out either “nil” or the last line of its own source code — again depending on which of two standards “rand” conforms to.

    But in reality, it doesn’t do that. Apparently, argv[0] has a different meaning in ruby than in every other language, and rand($.) means something nonstandard as well. The program actually chooses a random line from the first file given on the command line, and prints it.

    I’ve tried to translate it into similarly-styled python, but I honestly don’t understand the random line-choosing mechanism. So, here’s a conversion which is only somewhat similar: import sys, random result = random.choice(open(sys.argv[1]).readlines()) print result Can you tell me what this does, and how it works? Do you think a newb programmer could tell me?

    This little program worries me. I learned to read by programming, nearly a quarter-century ago, and have been programming somewhat seriously for 15+ years… but I couldn’t understand a 3-line program taken from the top google hit for “ruby examples”. I showed it to some other non-ruby programmers in case I was just being stupid, but they didn’t understand it either. Does that say something about Ruby?

    Granted, that’s only one data point, but it’s not a good start.

    Regarding Python’s style making your eyes bleed, you note: Ruby’s use of the end keyword to close a block of code … [is] the only obvious difference that comes to mind when I consider the matter.

    So. If you’re looking at a block of code too long to fit on one page, and the end of the block isn’t visible, does it make your eyes bleed? If the “end” keyword is off-screen, is that enough to give you a headache?

    OTOH, I find python’s official 4-space indent annoying. I prefer tabs (tabstop set to 2) for indentation, but I can see why they’re not the standard. They aren’t generally used as defaults due to the difficulty of entering them in non-programmers’ text editors, and their tendency to be invisible and wide.

    Indentation is always an annoyingly controversial style issue, whether it’s the amount per level being questioned, the bytes used to represent it, or the exact placement of block begin/end markers.

    Libraries / modules

    Perl definitely seems to hold the throne, since CPAN is so large. Python has a pretty sizable collection of modules too. I think a major reason for Python’s rapidly-growing library collection is the ease of wrapping other languages. Python, if I recall correctly, was originally designed to be an extension to C, and its C interfaces are particularly nice because of this. You can actually use it as a rich type system, object orientation layer, and memory management system for C… without ever caring that it has an interpreted, whitespace-nazi version too.

    Hey, look. Google to the resue. http://www.artima.com/intv/pyscale.html He wanted to “bridge the gap between the shell and C.”

    In addition to this, I’ve never seen an interactive programming shell that rocked my socks as much as Ruby’s irb. In fact, irb has become my calculator: it’s easier to use than any other shell’s tools, or even any command-line or GUI calculator applications, I’ve ever encountered, as a general-purpose calculator.

    The interactive programming shell is far from a novel idea, or a unique tool. Python has one which works about the same as irb, and many lisp systems have ones nicer than either python or ruby.

    As for the calculator, I’ve been using python for that purpose. It’s extremely similar to irb, except its prompts are shorter.

    Comments

    I think you’re the only person I’ve ever run across who thinks Ruby is less readable than Python. Not even serious Python hackers have expressed that opinion, in my experience.

    Sigh. Nobody listens to me. I find Python much more readable, and I’m pretty sure I’ve beat you over the head with it before. :)

    From Perl hackers, I hear that Ruby is more readable than Python.

    Well, duh. :)

    Anyway, sorry again for the book-full of zealotry inflicted on your blog. You’ve helped me to learn more about Ruby (and perl, for that matter), so I thank you. I enjoy this sort of discussion… I hope it’s perceived as discussion, instead of berating.

    Comment by ToyKeeper — 23 March 2006 @ 09:32

  8. ToyKeeper:

    I was a little confused about the purpose of the article. I couldn’t quite tell if it was a comparison of the differences between python and ruby, or if it was primarily praise for ruby with some added bits about python and perl. It seemed to drift back and forth.

    That’s because it did drift back and forth. I tend to meander a bit in purpose when I’m not getting paid for what I’m writing.

    I found the description of OOP requirements by Rees to be somewhat overwrought, particularly his point 9. On the other hand, I’m kind of a mediocre programmer at this point, and I’m less good with OOP than procedural programming, so maybe I’m just mistaken.

    You refer to “syntactic polymorphism” as an area where Ruby is lacking in comparison with Python. I’d like to see some further explanation of that, if you wouldn’t mind addressing it. From what I understand of what you said, it sounds like you’re describing something more like syntactic “monomorphism”, or something like that: for many cases, there is only one syntax, which may well be more Pythonlike than Rubylike: Ruby seems more likely to have many syntaxes for each case — or, perhaps more precisely, the ability to define new syntaxes for a given case, though not to the degree that Lisp supposedly provides.

    In your complaint about “symbols”, I’m not sure if you’re referring to actual Ruby symbols, or to the use of names for things like variables and constants. Perhaps you could elaborate.

    One thing to note, perhaps, is that Python does not inflict an OO approach.

    My experience is that Ruby doesn’t, either: it allows one to subsume its OO model beneath a procedural style, for instance, so that the fact you’re working with objects and methods can be ignored. See my above discussion of this state of affairs so that I don’t have to repeat it.

    One might compare this to the way Lisp is a functional language, but you can write code in it as though it, too, were an object oriented language. I refer you to Paul Graham’s writings on the subject (and, for that matter, to his brief mentions of Ruby to see his comparison of Ruby and Lisp).

    Could you list the specific Perl benefits, other than anonymous inline function definitions, which Python discards?

    Specifics are difficult, not being a Python programmer, but:
    • shortcuts
    • flexible style
    • flexible syntax
    • the ability to use either implicit or explicit elements

    I’m aware each of these could similarly be listed as detriments. I tend to be of the impression that they’re tools that can be used for good or evil, and have no character in and of themselves. Anonymous blocks are another of these and, like those I just mentioned, were discarded basically because someone didn’t like them. Python seems to have a rich tradition of discarding things because sometimes they’re misused.

    It’s often stated that Python is a good first programming language. But why is this? Could it be, in part, that its approach is a good match for how humans think?

    I don’t think so. I think it’s a good match for the way Pythonistas think, but that it’s a bad match for the way some other people think. It’s kind of a bad match for the way I think, which is better served by Perl, or Ruby, or even Objective C. In some ways, PHP and JavaScript are better matches for the way I think than Python, ignoring for the moment those languages’ limitations. On the other hand, Python may well be a better match for the way I think than Java.

    Your reference, as another possible reason Python is a good didactic language, to avoiding “unnecessary complexities” sounds like a typical statement from a Pythonista. The Python community judges something to be “unnecessary” because, in most cases, it is unnecessary. The language outlaws it, thus making it inaccessible even in the rare case that it does become necessary, or at least convenient, thus violating the Perl rule of making easy things easy and difficult things possible. As a form of self-justification, then, the Python community invents an answering rule that makes it “okay” or even “right” to make easy things difficult and difficult things impossible sometimes.

    It’s sort of a chicken and egg situation, really. I don’t know whether the restrictiveness of Python’s mechanism is the cause and the restrictiveness of the community’s policy is the effect, or the other way around. They certainly go hand in hand, though.

    Making something complex is easy. Making it simple is the hard part.

    I won’t argue with that. That doesn’t fully exlain the differences between Python and its rivals, however. To imply that it does is overly simplistic — like PHP, throwing the baby out with the bathwater.

    Actually, to extend the analogy a bit, it’s like there are twins in the tub. You’ve only thrown out one of the babies with the bathwater, while PHP throws them both out, along with the soap, the shampoo, the washcloth, and perhaps an older sister as well.

    Having a syntax that allows for more variation in how a program is written also allows for simplicity in the form of that program. Granting the programmer more tools isn’t an inherently “bad” thing, no matter how much the Python community takes it as a given. You don’t have to use every tool in every program, after all.

    Take a look around Barnes & Noble. Or, for that matter, take a look around the web. Look for some Java books, and you’ll most likely see much greater “weight and volume”, both for newb-level books and advanced topics. But does that make it a better language, or easier to learn? No, it just means the language is more popular.

    You’ve just entirely ignored the “friendliness” criteria I mentioned — and I don’t merely mean how friendly the authorial tone is. I also refer to the friendliness of the examples without dumbing them down so much that they’re useless for production code. Instructional texts about Java for non-programmers utterly fail to be friendly in that manner from what I’ve seen.

    I’ve been having a particularly hard time researching Ruby in order to compare it fairly, because I can’t find proper documentation for it. I’ve been looking for a decent source of Ruby documentation for a while… It must be A) free, B) online, C) up to date, and it must D) include a fairly complete reference of the language, its bundled modules/libraries, and its APIs for interfacing with other languages. But I have found nothing suitable.

    I, too, have not seen much of that sort of reference material. Ruby is rich in beginning-level references and instructional materials, and similarly poor in comprehensive expert-level references, from what I’ve seen. Of course, I think I commented on the maturity of Ruby’s documentation somewhere back there.

    a huge chunk of the material there is available on your computer, from inside python itself

    As I mentioned to you in IMs, you should have a look at Ruby’s ri, an electronic reference wherein you can basically ask Ruby about itself for fairly comprehensive information about the language’s syntax, et cetera. It seems to solve this problem for you.

    Which good habits, other than indentation, does Python enforce?

    Try taking the role of a Perl programmer and ask a Pythonista some time, without referring them to anything like this discussion. Make it sound, while you’re asking, like enforcing good habits is a good thing, and like you don’t believe Python really does that. Then sit back and listen. I hope you remembered to pack a lunch.

    Also, can you list an example of when you would intentionally want not to use good indentation?

    This is a prime example of the color of the Python Kool-Aid showing in your eyes. It’s not that you would “not want to use good indentation”, it’s that the PYthon language designers have the self-satisfied sense of superiority to assume they know what “good indentation” is in all cases, all the time, without even knowing what program I’m trying to write and what sort of code I’m writing to accomplish the task.

    “Good indentation” can be different in one instance than in another. At one point in this commentary of yours, even you complain about the four-space standard for indentation which, while not enforced specifically by the language, at least indicates that “good indentation” is not a static concept.

    BTW, have you noticed any of the style-related things Ruby enforces? For example, ever seen it dump this error? “warning: don’t put space before argument parentheses”

    Does the program still run?

    In relation to methods vs. functions:

    In practice, the only difference is whether you type “dostuff” or “obj.dostuff”, which as far as I can tell is handled the same way in Ruby as in Python.

    That may be the only difference in calling syntax, but it’s not the only difference in behavior, and I think you know it — probably rather better than I do, even.

    As far as I can tell, when using OOP techniques, it’s best to use them consistently. In other words, either write OO code, or don’t. Half-measures tend to suffer OOP’s limitations without gaining all of its benefits.

    The only time you’ll see “dostuff” in Ruby without the object prepended, as far as I’m aware, is when the object to which you’re sending a message is quite obvious and thus can be referenced implicitly (such as when the object is “self”).

    By the way, I find it odd that you can in one (virtual) breath you can discuss the differences between functions (non-OOP elements) and methods (OOP elements) in Python, then in the next suggest that Python doesn’t have any non-OOP elements.

    Re: my commentary about the pervasiveness of OOP characteristics in Ruby — Apparently, I chose a poor example. I was not aware that operators in Python were actually syntactic sugar for methods, probably because unlike in Ruby they’re syntactic sugar rather than being the methods themselves. In Ruby, the “syntactic sugar” element consists only of replacing the dot with whitespace, while in Python the actual characters typed to refer to the method themselves are the syntactic sugar.

    I’m not sure which approach is the more convenient and advantageous, but Python’s approach seems a touch more needlessly complex from my perspective, and is certainly a bit more surprising (thus violating the POLS, as far as I can tell), even within the context of the language’s own conventions.

    Also, underscores suck.

    re: style and syntax —

    There are definitely strongly-encouraged policies, but those are not the strict mechanisms you seem to be describing. For example, nearly all python code uses “self” for a method to refer to its instance object, but you could call it “this” or even “bob” if you wanted to.

    Perhaps the required use of “self” (or “this” or “bob”) is itself a bit too rigid.

    What I wonder is why you consider Ruby more readable than Python. I haven’t found it to be, but perhaps I’ve been looking at the wrong code.

    I find foo more readable than bar. I also find baz more readable than qux.

    I couldn’t understand a 3-line program taken from the top google hit for “ruby examples”.

    I bet I could find a three line program taken from the top Google hit for “python examples” that I wouldn’t understand, too. So what?

    If you’re looking at a block of code too long to fit on one page, and the end of the block isn’t visible, does it make your eyes bleed? If the “end” keyword is off-screen, is that enough to give you a headache?

    That doesn’t even deserve a response, and I think you know it.

    OTOH, I find python’s official 4-space indent annoying. I prefer tabs (tabstop set to 2) for indentation, but I can see why they’re not the standard. They aren’t generally used as defaults due to the difficulty of entering them in non-programmers’ text editors, and their tendency to be invisible and wide.

    It’s more than that, I think. Python (like Perl) seems most readable with a four-column indent, while Ruby seems most readable with a two-column indent. I think the difference has something to do with the arcing effect in the left-hand column created by Ruby’s use of the end keyword, while Python needs greater differentiation between levels of indentation due to the fact that there is not a visual hint in the form of an end-delimiter when using nested indents. I haven’t figured out yet why Perl doesn’t seem to work as well with only two-column indents (for me at least), unless it’s simply that a closing brace isn’t a high enough visibility delimiter.

    Indentation is always an annoyingly controversial style issue, whether it’s the amount per level being questioned, the bytes used to represent it, or the exact placement of block begin/end markers.

    . . . or even the mere existence of block begin/end markers, apparently. In fact, maybe the lack of end delimiters for blocks of code are part of the reason that multi-line anonymous blocks are verboten in Python: once you remove the beginning delimiter for a block of code, your anonymous block just looks like a paragraph floating around out in space without any kind of logical connection to anything. Wouldn’t that be a sight to see . . . ?

    You can actually use it as a rich type system, object orientation layer, and memory management system for C… without ever caring that it has an interpreted, whitespace-nazi version too.

    Why bother? We already have Objective C.

    The interactive programming shell is far from a novel idea, or a unique tool.

    I didn’t say that irb invented the interactive programming shell — just that it “rocks my socks” more than any other specimen I’ve encountered. I’m actually rather disappointed that psh is so much more clunky to use than irb, despite the fact that I know Perl quite a bit better than Ruby.

    Anyway, sorry again for the book-full of zealotry inflicted on your blog. You’ve helped me to learn more about Ruby (and perl, for that matter), so I thank you. I enjoy this sort of discussion… I hope it’s perceived as discussion, instead of berating.

    No problem. While I definitely get the impression you’ve been drinking some of the Python Kool-Aid (though not as much as some others I’ve met), I don’t get the impression you’re just being a prick about it. Go ahead and challenge my ideas. Without challenging the old, we can’t make room for the new, or be sure that the old is really any better without seeing it weather the challenge.

    I’ve been learning a bit from these discussions as well. Since I’m learning, and you’re learning, I think there’s little more for which I could ask.

    Comment by apotheon — 24 March 2006 @ 04:03

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

All original content Copyright Chad Perrin: Distributed under the terms of the Open Works License