Monday, September 07, 2009

The Python Ternary Sucks

I have just spent an hour trying to track down a weird bug in some Javascript interpolation code. The offending code looks like this:
var n = i+1?i<length:length;
Ternary expressions almost always have the structure:
condition value_if_true value_if_false
My Python brain got it wrong, as you can see, I used:
value_if_true condition value_if_false
in the Javascript code, which is the Python way of doing things. Eg:
i+1 if i < length else length
Stupid Python. In this case, The language syntax of Python should have followed the conventions used by most of the planet. "Practicality beats Purity" and all that.

Edit:To be clear, my point is that the Python Ternary expression is needlessly different to almost every other language and causes Brain Pollution.

</rant>

14 comments:

Noufal said...

Interesting. I wasn't even aware of the ternary in Python. I usually do this instead

condition and value_if_true else value_if_false

Sort of like

attribute = t.attr and t.attr else "default"

Martin said...

I think both the C/Javascript way and Python way make sense.

I read the javascript as:
is the condition true? then use A, otherwise B

And the python way as:
use A if the condition is true, otherwise use B

Francesco said...

I like Python's way. It allows you to express what you think will be the "right" branch, since you read the value_if_true first.

Brandon Rhodes said...

I like the Python version better, because it keeps the two options next to the condition itself, rather than throwing one of the two possible results way over on the other side of the positive result. It is easier to see the condition as controlling both of the possible expressions, with one in its left hand and the other in its right.

Plus, it reads very cleanly as English grammar, and readability is one of the things that drew me to Python in the first place.

I think the "cond and v1 or v2" (surely @Noufal doesn't really mean that he says "else" in the middle of a Python expression? I think we see one weakness of the construction already, which is that it's hard to remember exactly how to do it correctly!) is something of a disaster. First, because it doesn't say what it's doing, unlike good Python code; it's a trick, and you have to know the trick to read it quickly. Second, it only works if "v1" turns out to be something that evaluates true. If "v1" happens to be false — maybe because it's a fancy container object that's not yet filled with objects, or whatever else can cause bool(obj) to be false — then the test falls through and you get v2 anyway, despite the condition's being true. Someone who doesn't know this is a "trick" will (I know from experience!) stare, trying to figure out why the test of v1's truthfulness is so important to the programmer.

I think the C/Java expression is fine too, because "?" always *ends* a question in English. So, if you see "a?b:c", then you know "a" must be the "question" that's being asked, because it ends with a question mark. It's a bit clunky to use special symbols rather than readable words, but, hey, it wouldn't be a C-family language without that. :-)

Brandon Rhodes said...

Another thought about @Noufal's proposal: if he really wants to say:

t.attr and t.attr or "default"

then he can instead shorten it to:

t.attr or "default"

and not incur the expense of asking the "t" object for its "attr" twice, which could be an expensive proposition if the attribute is dynamically generated.

Travis Bradshaw said...

That's sort of a poor standard of measure. If there was any responsibility (or even desire) for all programming languages to be syntactically compatible... then they just be the same language.

I also "think" in Python, and it makes for poor Javascript now and then, but it's never even crossed my mind to blame Python for my Javascript mistakes. If anything, it just makes me a little "homesick". If Python didn't have those differences, I wouldn't have fallen in love with it in the first place.

René Dudfield said...

I hate ternary code all together. It's very often a source of bugs, and it's usually hard to read.

rgz said...

I love python but I agree that this is an unnecessary difference as in, the benefits of the different syntax don't make up for the problems caused by it.

Personally I wanted something like:

foo = if a > b: a else: b

with the option of spacing like:
foo = if a > b:
a.longmethod()
else:
b.anothermethod()

but then again I wanted list comprehesions to be written as:
(for item in sequence: item.sometrasformation())

Evan said...

I have to completely disagree with this post. Not only did Python's ternary syntax allow for easy reading, but it also didn't require the addition of any more symbols.

Seriously, if you look at what you call "common" ternary syntax, it's pretty difficult to understand unless you have background with it. Why does Python have to conform to other languages? People are also used to braces, but it doesn't mean that they should be used in every language.

rgz said...

Because not using braces improves readability (and writablility) more than the alternative, this ternary operator not quite as much.

I mean if the result of an expression depends on a conditional I want to see the conditional first, or at worst, last, grouping the alternatives together and making the conditional stand out.

This ternary spreads the alternatives and buries the conditional, and on top of that it is non-standard (I repeat: "on top of that", it's not the main issue).

Its only redeeming feature is that it reads nice in English but Python is not English, if it was we would:

CALL METHOD SIT OF DOG WITH PARAMETERS OF 5 AND "minutes"

And while I understand the aversion to adding symbols, the reason ternaries are so compact is because the arguments usually are very compact themselves:

I'm of the opinion that ternaries might as well be dropped from the language. What we really needed is a terser lambda syntax.

foo = if(a > b, ?x, ?y)

Makes more sense and it's extensible, but we'd be navigating to much into lisp territory

Evan said...

> Because not using braces improves readability (and writablility) more than the alternative, this ternary operator not quite as much.

The Python ternary operator does improve readability and writability once you're used to it.

Anonymous said...

I was going out that your mother sucks, too.

rgz said...

Ignoring anonymous. Yes, everything is easier once you get used to it, the question is is it better in itself?

In algol languages I often do:
var area = typeof(s) == 'Figure'?
s.getArea():
s.width * s.heigth;

In python what is the alternative?

area = (s.getArea()
if typeof(s) == 'Figure'
else s.width * s.heigth)

It doesn't seem right. I'd rather do the more verbose:
if typeof(s) == 'Figure':
area = s.getArea()
else:
area = s.width * s.heigth

PD: the expressions themselves are not important, yes I know I should use EAFP that's not the point.

GM said...

Syntactical tastes aside, it strikes me as criminal that there isn't _some_ kind of type warning when one side of the fork returns a boolean and the other an integer.

This would also apply to using an integer as the condition, if it weren't for the language feature that allows that. At the risk of offending those who still think digital watches are a pretty neat idea: This "feature" is horribly antiquated - it was a neat optimisation once upon a time, but compilers have been smart enough optimise "x!=0" themselves for forty years now.

Popular Posts