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.
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.