2007-06-23

Python: The Middle Child of Interpreted Languages

Oh, Python. Such promise. Such disappointment.

Python was presented to the world as a great way to out-Perl Perl. "It's easy! It's fast! It's OO!" they cried, their voices pregnant with raw enthusiasm, anxious rivulets of spittle forming in the corners of their open, praising mouths.

I tried it. I drank the Kool-Aid. Python has a bigger amount of useful stuff built into it, things like a decent string class and some nice support for kooky numeric tricks and some Big Damn Integers. These are all things that Perl has, or could have, if CPAN is your friend.

Don't get me wrong: CPAN most assuredly is your friend, but Perl is quite limited in the things that it can do when not accompanied by a galaxy of cool modules. The strength of Perl is in its user-supported modules, in part because the community is so good, and in part because Perl really needs the help.

Python, sadly, lacks the modules, lacks the awesome majesty of CPAN, and needs just as much help as Perl does. It has a stronger focus on objects, which is great, albeit this makes the language capable of some remarkably awkward prose, like calling the join() method of a string in order to use it as a delimiter for joining an array:

>>> array = ('one','two','three')
>>> array
('one', 'two', 'three')
>>> ",".join(array)
'one,two,three'

Coming from a Perl and C background, I can say that calling join() as a method from a string is just plain freaky. The sorry mess of Python's modules (and the blasphemy of its documentation) makes it barely more useful than a fancy calculator. There are so many unsolved problems in porting Python to multiple architectures (problems that Perl solved years ago, I might add) that there can be no reliable means of distributing non-canonical modules. The very best technique that Python can offer is "python ./setup.py install", and God help you if it doesn't work right the first time.

Python also has the unfortunate tendency to make you choose whether a variable is a string or an int, without letting you staticly type it ahead of time. This means that Python will not let you declare a variable as an integer data type, but you can assign an integer later on, by accident or otherwise. Oh, did I mention that Python has overloaded operators that make the situation even worse?

>>> a = '123' 
>>> b = '345'
>>> a + b
'123345'
>>> a = 123
>>> b = 345
>>> a + b
468

In order to switch back and forth, you end up using int() and str() everywhere. But not "a.int()" or "b.str()", of course. That would make sense. Ruby has solved this by (logically) making a built-in method for most of its data types called "to_s". This doesn't alleviate the problem of having to convert your data back and forth in Ruby when Perl can intelligently do it automatically, but at least the language isn't hypocritical about it.

Python has easy object-oriented support. Take our Human class. In Perl and Ruby, we have previously created a simple class that creates a new custom data type to let us group together an age and a name. In Perl, this was like pulling teeth, requiring not only a new package but a proto reference, building our own refs to store data, and then blessing everything we care about into the class. Remember how painful that was? We were constantly $self->{this}ing and $self->{that}ing. Python is better, but only slightly:

class Human:
  def __init__(self):
    age = None
    name = None

Python's only saving grace is that it avoids the need to create an explicit accessor. We don't need to conjure some method to set or get our class's internal data. This has its disadvantages:

>>> h = Human()
>>> h.age = 18
>>> h.name = "Jim Smith"
>>> h.sex_with_donkeys = True

What? Python promotes its promiscuous classes as "useful" data types to have, like a Pascal record or a C struct.

I don't know any C structs that let you fuck with them like that.

Granted, this type of open data structure is great if you're lazy. And I am. It's ideal for being able to throw a bunch of crap together and use it as a single data type without a lot of heavy lifting, but this is hardly a good way to make sure your program is doing what you expect it to do. It's also bad for consistency: if you can have a Record class, how will you ensure all your Records contain all the same stuff? Officially, the Python language does not have a "defined()" function and the community's stance towards this problem is, and I am not making this up, that you should always know what your data types look like so that you never call a member that might not exist.

Python is also badly configured to insist that all class methods include self, meaning that Guido van Rossum at some point said "I want to be totally different from Perl, but not totally totally different." I find that the presence of a self variable in a class is a good indicator that the OO interface is smoking crack.

Python strictly enforces its program structure by means of whitespace. The idea is that consistent whitespace improves readability of code and removes ambiguity in the language, making writing Python "better" than other languages because, well, if you don't follow the indentation rules, you can't play the game. With the whitespace restriction in place, you'd think that the data types a class method would edit would be, y'know, implied.

Python is entering its awkward phase. It's old enough to have fixed the bugs that it had in its early 1.x days, but too mature to fix some of the major architectural flaws that its proponents have grown to know and love. Every language stalls at some point, and though the specification marches on carried by a handful its most stalwart bureaucrats, the momentum of the language is almost zero. Think of the difference between Fortran-77 and Fortran-90. Perl 5 is the end of the line and nothing Perl 6 (or Haskell) could do will keep Larry Wall's onion from peeling away to nothing. Python and Ruby have yet to hit their peaks, but trust me: they're there, and they're coming. For Python, the end really can't come too soon.

No comments: