Wednesday, July 23, 2008

Annoyances with Blogger and Python

In case any of you are actually reading this blog and are eagerly awaiting an installment in the promised Drugs/God/etc series, I posted it ages ago. But because I'd drafted a sentence in it before I wrote my last post, Blogger decided to put the new post underneath the less-recently-published-but-more-recently-started one. I can see how this could be potentially maybe useful, but man its annoying when you're not expecting it and there's apparently no way to undo it.

Also, on a very different tack, I've become utterly convinced that at least one criticism leveled at my favourite overall programming language, Python, is utterly true and fair. After quite a while away from writing Python code, I started last night on a whim to knock up some code for a prototype of an idea I once had. It's going swimmingly; the Python Image Library, which I'd never used before, seems quick, intuitive, and with the all the features I need for this project. As for Python itself, well, my heart still belongs to whitespace delimitation. All the basics of Python coding are there in my mind like I never stopped using them, or like I've been programming in this language for 10 years.

Except when it comes to Classes. I added some classes to code that had previously just been functions, and you know what I did - or rather, forgot to do? Put in the 'self'. In front of some of the variable accesses, but more noticably, at the start of every single method argument list. This cannot be any longer blamed as a hangover from Java - I've written a ton more code, more recently in Python than in Java or any other OO language. What's more, every time I go back to Python after a break of more than about a week or so, I start making this 'mistake' again. The reason - that old Python favourite, "Explicit is better than implicit."

I'm sorry, but EXPLICIT IS NOT NECESSARILY BETTER THAN IMPLICIT. Assembler is explicit FFS. Intuitive, clever, dependable, expected, well-designed implicit behaviour is one of the chief reasons why I use a high level language. Implicitly garbage collect old objects for me? Yes, please!

I was once bitten by a Python wart I felt was bad enough to raise and spend considerable time advocating change for on comp.lang.python (never got around to doing a PEP; partly laziness, partly young and inexperienced enough to be intimidated at the thought. Still am, perhaps.)

The following doesn't work as any sane, reasonable person would expect:

# Blog code, not tested
class A():
def __eq__(obj):
return True
a = A()
b = []
assert a == b
assert not (a != b)

The second assertion fails. Why? Because coding __eq__, the most obvious way to make a class have equality based comparisons, buys you nothing from the != operator. != isn't (by default) a synonym for the negation of == (unlike in, say, every other language ever); not only will Python let you make them mean different things, it actively encourages you to do so.

There were a disturbingly high number of people defending this (including one quite renowned Pythonista, think it might have been Effbot). Some had the temerity to fall back on "Explicit is better than implict: if you want != to work, you should damn well code __ne__!"

Why, for heaven's sake, should I have to, when in 99.99% of use cases (and of those 0.01% instances quoted in the argument at the time only one struck me as remotely compelling) every programmer is going to want __ne__ to be the logical negation of __eq__? Why, dear Python, are you making me write evil Java-style language power reducing boilerplate to do the thing you should be doing yourself anyway? What's more every programmer is going to unconciously expect it to work this way, and be as utterly as mystified as me when it fails to do so? Don't tell me to RTFM and don't tell me to be explicit. I'll repeat myself - if I wanted to be explicit, I'd be using C and managing my own memory thank you very much. Better yet, I'd explicitly and graphically swear - swear in frustration at this entrenched design philosophy madness that afflicts my favourite language.

I think the real problem with the explicit is better than implicit, though, is that while you can see the underlying truth its trying to get at (which is perhaps better expressed by Ruby's more equivocal less dependable but more useful Principle of Least Surprise), in its stated form its actually kind of meanginless except as a wart - no, we'll call it for what it is, a language design bug - defence.

You see, the problem is, there's no such thing of explict in programming. Its not a question of not doing things implicitly; its a question of doing the most sensible thing implicitly. For example this python code:

some_obj.some_meth(some_arg1, some_arg2)

is implicitly equivalent to

SomeClass.some_meth(some_obj, some_arg1, some_arg2)

which in turn gives us self as a reference to some_obj, and Python's OO model merrily pretends its the same as Java's when in fact is a smarter version that just superficially looks the same.

The problem is that the explicit requirement to have self at the start of every method is something that should be shipped off to the implicit category. You should have to be explicit, yes - explicit when you want the other behaviour, of self not being an argument, because thats the more unusual, less likely case.

Likewise,

a != b

is implicitly equivalent to something like calling this function (may not be correct, its a while since I was heavily involved in this issue):

def equal(a, b):
if hasattr(a, "__ne__"): return a.__ne__(b)
if hasattr(b, "__ne__"): return b.__ne__(a)
if hasattr(a, "__cmp__"): return not (a.__cmp__(b) == 0)
if hasattr(b, "__cmp__"): return not (b.__cmp__(a) == 0)
return not (a is b)

There's absolutely nothing explicit about this. I wasn't arguing for making behaviour implicit; I was arguing for changing the stupid implict behaviour to something more sensible and less surprising.

The sad thing is there are plenty of smart Python programmers who will justify all kinds of idiocy in the name of their holy crusade against the implict.

If there was one change I could make to Python, it would be to get that damn line out of the Zen.

8 comments:

Joel Nothman said...

No, of course that code wouldn't work! It has no indents...

Joel Nothman said...

Although I'm actually confused about the second long code snipped you have presented there... Surely the name function name "equal" contradicts all the other lines in the code... And you don't even make use of __eq__ there, which seems to be going against your argument. !?

With Respect to X said...

Trust me, I put indents in. Blogger/the web removed them.

Sorry, yes, the function name should have been notequal (notice I am referring to the != operator) But really the name of the function is immaterial. Call it what you want, it does more or less what the != operator does.

With Respect to X said...

P.S. It would seem the pythonista in question may well have been Effbot - he was quick to post a pretty dismissive reply when I put this post on comp.lang.python :-)

Joel Nothman said...

Well, Blogger certainly has ways to: (a) post code, indents and all; (b) set dates for your posts so that they appear in order.

Unfortunately, I don't use Blogger and hence don't have the keys to either.

James Haggerty said...

Gazing longingly at the 'What's New in Python 3.0' page I noticed:

"!= now returns the opposite of ==, unless == returns NotImplemented."

Happy now? Eh?

With Respect to X said...

Yes. Very happy :-)

Of course by the time Python 3.0 hits production grade release, I'll have finished Rosetta anyway :P

Brandonswfd said...

Trust me, I put indents in. Blogger/the web removed them. Sorry, yes, the function name should have been notequal (notice I am referring to the != operator) But really the name of the function is immaterial. Call it what you want, it does more or less what the != operator does.