Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Defending the ternary operator

4 views
Skip to first unread message

crib...@mail.inet.com.br

unread,
Feb 7, 2003, 11:26:02 PM2/7/03
to
To all Pythonistas,

I'm writing this to sum up my feelings regarding PEP 308 and some of the
arguments presented (pro and against). It will probably raise one or two
flame-like answers but I'm getting used to it :-)

Why is a ternary operator needed? The main reason is that it allows the
programmer to clearly express his intentions. The alternative idioms that
are frequently presented in c.l.p just doesn't cut it; they're mostly
clever hacks that abuse some side effects of the way logical operators and
expressions work in Python.

The main example are abuses of the following definition: false=0, true=1.
Most alternative solutions proposed to avoid the inclusion of the ternary
operator abuse this fact, either through indexing or by a clever
composition of logical operators. While I agree that the convention is a
fact of life, and that it does have solid foundations on binary logic and
past implementations of several languages, it does not seem right to abuse
it like this; it requires the programmer to be aware of this identity,
which is not obvious for beginners.

Many people say, 'explicit is better than implicit'. Well, idioms such as
[a,b][cond] are implicitly using the identity mentioned above. The same
goes for the and/or hacks: clever stuff, but not immediatelly obvious
unless you execute it mentally.

That leaves us with two main options: (a) traditional if-then-else
statements setting temporary variables, or (b) creating a brand new
ternary operator (as proposed by Guido).

Traditional if-then-else statements have their own problems. They're
lenghty for simple stuff, and may obfuscate the program logic. It would be
much better to use if statements only for 'big' decisions, the ones that
define the program's logic; small stuff such as choosing between two
representations to format data can be efficiently written as an inline
expression, leaving the logical flow of the program cleaner.

Now, look at it this way: the ternary operator is just 'syntactic sugar'.
It will make code easier to read. Almost every ocurrence of the ? operator
in C programs occur in short expressions, things that are much better
written inline than spread out over 4 lines. It can be abused, but many
other constructs can be abused if nested arbitrarily, and this does not
makes such constructs bad by themselves.

I also urge everyone on the list to be careful when considering options.
The automatic (but unconscious) reaction for many people is to stand up
against change. An open stance is important now, to careful evaluate all
the arguments. Eventually, some people may give it a try and rewrite some
of their actual code, just to see would it end up looking like.

I myself felt victim of this trap after reading Guido's email. I thought
that the syntax proposed was strange; a little weird, confusing, because
of the evaluation order. But the more I read it, the better it reads.


Carlos Ribeiro
crib...@mail.inet.com.br

Laura Creighton

unread,
Feb 8, 2003, 2:01:09 AM2/8/03
to
Regardless of the reasons some people _want_ conditionals, people
are going to _use_ them because they save keystrokes. The number of
people who will do anything to save keystrokes is legion.

Laura

Ian Bicking

unread,
Feb 8, 2003, 2:58:16 AM2/8/03
to

Why do you lack such faith in programmers? Python is the programmer's
friend, not the programmer's keeper. What trauma you have suffered at
the hands of inferior code should not blind you. Beautiful code is
something that is nurtured and *created*. You cannot attain it by
forming an environment where all bad techniques are outlawed, based on
the expectation that given no other options the bad programmer will
somehow write beautiful code. It would be like taking a cast of a
beautiful orchid, then seeding a ragweed beneath the cast. The ragweed
will not become an orchid simply because no other shape is allowed it.

Ugly code can be thrown away. That task is easy. Our efforts should
focus on the beautiful code, because that is the difficult task and the
worthwhile task. We should judge this extension to the language with
our aspiration, not with our fears.

--
Ian Bicking ia...@colorstudy.com http://colorstudy.com
4869 N. Talman Ave., Chicago, IL 60625 / 773-275-7241
"There is no flag large enough to cover the shame of
killing innocent people" -- Howard Zinn

Laura Creighton

unread,
Feb 8, 2003, 4:00:16 AM2/8/03
to
> On Sat, 2003-02-08 at 01:01, Laura Creighton wrote:
> > Regardless of the reasons some people _want_ conditionals, people
> > are going to _use_ them because they save keystrokes. The number of
> > people who will do anything to save keystrokes is legion.
>
> Why do you lack such faith in programmers?

Because I am one. Because I know so many of them. Because 'lazy
work' and 'hurried work' has been the bane of all creators since we
first started creating anything.

Laura

Paul Rubin

unread,
Feb 8, 2003, 5:40:59 AM2/8/03
to

Why do you think saving keystrokes impairs readability? Usually, for
me at least, a shorter program is easier to read, not harder.

Laura Creighton

unread,
Feb 8, 2003, 6:30:29 AM2/8/03
to
> --
> http://mail.python.org/mailman/listinfo/python-list

I don't think that the relationship is that simple. Some things which
are elegant are short. Some things which are obscure are also short.
Persuing shortness alone is not the answer, or we would all be programming
in APL.

Laura

John Roth

unread,
Feb 8, 2003, 9:39:04 AM2/8/03
to

"Laura Creighton" <l...@strakt.com> wrote in message
news:mailman.1044688340...@python.org...

Hello? Laura?

Python is a language for adults, not people who have to have
their hands held by the gestapo.

John Roth
>


Andrew Koenig

unread,
Feb 8, 2003, 10:24:12 AM2/8/03
to
Paul> Why do you think saving keystrokes impairs readability?
Paul> Usually, for me at least, a shorter program is easier to read,
Paul> not harder.

As I noted (with variations) in another thread, I think that
readibility is proportional to brevity * familiarity.
So something short but unfamiliar is hard to read until you
become familiar with it.

The essence of Laura's argument, I think, is that many programmers
will not become familiary with if-expressions, as they're used only
from time to time, which means that when they do encounter them,
it will be a mental stumbling block.


--
Andrew Koenig, a...@research.att.com, http://www.research.att.com/info/ark

Andrew Koenig

unread,
Feb 8, 2003, 10:27:45 AM2/8/03
to
Laura> I don't think that the relationship is that simple. Some
Laura> things which are elegant are short. Some things which are
Laura> obscure are also short. Persuing shortness alone is not the
Laura> answer, or we would all be programming in APL.

If APL had not been so intransigent about avoiding control structures
and abstract data types, I think it would be orders of magnitude more
popular than it is today.

APL is a prime example of my claim that ease of comprehension is
proportional to brevity * familiarity: APL programs tend to be
extremely short, but because most of the functions are expressed as
symbols that give outsiders no clue to their meanings, they are
unreadable until you put in the effort to learn what the symbols mean.

Once you do know what they mean, APL programs are often much easier
to understand than their counterparts in other languages.

Anthony Baxter

unread,
Feb 8, 2003, 10:39:17 AM2/8/03
to

>>> Andrew Koenig wrote

> The essence of Laura's argument, I think, is that many programmers
> will not become familiary with if-expressions, as they're used only
> from time to time, which means that when they do encounter them,
> it will be a mental stumbling block.

There's also the point that "if" already has existing meanings. This
means that it's use in this rare form (at least, I'd hope it's rare;
if a piece of code features a large number of ternary expressions,
I'd question the sanity of the author), it will confuse the reader
even more.

Anthony


Andrew Koenig

unread,
Feb 8, 2003, 11:27:36 AM2/8/03
to
Laura> Because I am one. Because I know so many of them. Because
Laura> 'lazy work' and 'hurried work' has been the bane of all
Laura> creators since we first started creating anything.

As an experiment, I went back through a 2800-line C program that I
wrote about 20 years ago (!) to see how often I used ?: and in what
context. I figure that reading my own 20-year-old code is probably a
lot like reading some else's code.


Here are all the places in which I used the ?: operator.


EXAMPLE 1:

p = <null-terminated string>;
q = <fixed-length char array>;
for (i = 0; i < sizeof(<fixed-length char array>); i++) {
if (*q++ != (*p? *p++: ' ')) {
/* Complain that the two strings aren't equal */
}
}

As a C programmer, I have no trouble understanding what's going on:
The "if" compares the next character in q with either the next
character in p or a blank, depending on whether we've hit the null
that terminates p.

If I didn't have ?:, and I wanted to rewrite the if statement to have
the same effect, I think I would do something like this:

p = <null-terminated string>;
q = <fixed-length char array>;
for (i = 0; i < sizeof(<fixed-length char array>); i++) {
char c = ' ';
if (*p)
c = *p++;
if (*q++ != c) {
/* Complain that the two strings aren't equal */
}
}

EXAMPLE 2:

mode = cvlong(param, strlen(param), 8) |
(c == 'c'? S_IFCHR: S_IFBLK);

Before you flame at me for using an absolute constant here, I should
point out that the "8" in the argument to cvlong means "Treat this
as an octal number, please." Again, without ?: I might have written

mode = cvlong(param, strlen(param), 8);
if (c == 'c')
mode |= S_IFCHR;
else
mode |= S_IFBLK;

EXAMPLE 3:

fprintf(tf, "%c %#o %d %d ",
mode == S_IFBLK? 'b': 'c',
buf->st_mode & 07777,
major(buf->st_rdev),
minor(buf->st_rdev));

Without ?: I would either have to introduce an extra variable or
write something like this:

if (mode == S_IFBLK)
fprintf(tf, "b");
else
fprintf(tf, "c");
fprintf(tf, " %#o %d %d ",
buf->st_mode & 07777,
major(buf->st_rdev),
minor(buf->st_rdev));

EXAMPLE 4:

fprintf(stderr, "package %s\n",
strcmp(pack->iname, instr)? pack->ename: instr);

I would never write code like this today, because it uses strcmp as a
condition? When you do that, the condition is true if the strings are
*un*equal, a fact that is easy to forget. But let's brush that
aside, and see how to rewrite it without ?:. I get this:

if (strcmp(pack->iname, instr))
fprintf(stderr, "package %s\n", pack->ename);
else
fprintf(stderr, "package %s\n", instr);

EXAMPLE 5:

fprintf(file,
*p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
c);

This code is a little sneaky. It's in the middle of a routine that
writes a printable representation of an arbitrary string. So what it's
doing is peeking ahead one character. If that character is an octal digit,
it's writing the present character out as a 3-character octal escape;
otherwise it's using the shortest escape possible. In other words, this
code would represent ^Gx as \7x but ^G6 as \0076, taking advantage of the
fact that octal escapes are limited to three characters.

Again, let's brush aside questions about what the code is trying to do,
and consider how it does it. Without ?:, I think it would look like this:

if (*p >= '0' && *p <= '7')
fprintf(file, "\\%.3o", c);
else
fprintf(file, "\\%o", c);

EXAMPLE 6:

p = (s == NULL)? str: s;

This example sets a default value to substitute for s in case s is NULL.
The likely rewrite:

if (s == NULL)
p = str;
else
p = s;

EXAMPLE 7:

Here, hrout and mrout are functions:

r = (*(line[0] == '.'? mrout: hrout)) (line, out);

So it's choosing which function to call based on the value of line[0].
The rewrite:

if (line[0] == '.')
r = mrout(line, out);
else
r = hrout(line, out);


So... I used ?: 7 times in 2800 lines, or once every 400 lines or so.


Looking back on these examples, I find that in the first five of them,
the use of ?: makes the code easier to understand, even after 20 years.
For #6, I think it's a wash. For #7, I think the rewrite is slightly
easier to understand, but not because of the ?: -- rather, the rewrite
is easier because it doesn't use the notion that the name of a function
decays into a pointer to the function. In Python, I'd prefer the original.

Alexander Schmolck

unread,
Feb 8, 2003, 11:50:06 AM2/8/03
to
Andrew Koenig <a...@research.att.com> writes:
> As I noted (with variations) in another thread, I think that
> readibility is proportional to brevity * familiarity.

Almost, but not quite (see below).

> So something short but unfamiliar is hard to read until you
> become familiar with it.
>
> The essence of Laura's argument, I think, is that many programmers
> will not become familiary with if-expressions, as they're used only
> from time to time, which means that when they do encounter them,
> it will be a mental stumbling block.

I think you're missing one important aspect: predicatability of the sort that
syntatic uniformity buys you. That is because it allows you to parse larger
chunks, e.g. you always know that when you run into the token "if" the context
is as follows: ``if CONDITION: DO_SOMETHING ...``.

Introducing an "if" expression essentially means that both forms will be used
interchangably for a certain percentage of cases, which I'd guess, makes *all*
if expressions more difficult to read (at least once interchangable use of
both forms has reached a certain frequency -- if that ever happens (as I think
it has in Ruby and Perl, BTW)).

That is not to say that your point about familiarity (and thus the trade-off
between catering to new or infrequent users and power users) isn't
valid. Someone who already mastered all the "old" features has much more to
gain from the introduction of partly overlapping new ones than someone who
hasn't -- which is one of the reasons why languages start out small and bloat.

Indeed, although my feeling is that introducing a "ternary operator" to python
would do a disservice to the language, I'd be more than happy to use it
myself.


alex

Andrew Koenig

unread,
Feb 8, 2003, 11:40:37 AM2/8/03
to
>> Once you do know what they mean, APL programs are often much easier
>> to understand than their counterparts in other languages.

Laura> Then you go away for six months and code in some other language.
Laura> And then you get a bug report. You look at your code and no longer
Laura> remember what the heck it was that you were trying to do.

In 1976 I wrote an entire student-registration system in APL.
In 1977, not having looked at the code for a year, I was brought
back to make some major changes to it.

I had no trouble understanding what the code was doing.

From that experience, I can say confidently that APL's lack of
data-abstraction facilities is a much more bigger problem than its
brevity.

Incidentally, that code was still in use when I checked back 15 years
later.

Laura Creighton

unread,
Feb 8, 2003, 10:54:36 AM2/8/03
to

Then you go away for six months and code in some other language.


And then you get a bug report. You look at your code and no longer

remember what the heck it was that you were trying to do.

I think APL demonstrates that the goals of 'people who write code'
and the goals of 'people who read code' need not coincide. Python
used to be heavily weighted in favour of those who read code. Many
people I know thought that this was the best thing about Python.

Laura

> --
> http://mail.python.org/mailman/listinfo/python-list

Piet van Oostrum

unread,
Feb 8, 2003, 12:11:00 PM2/8/03
to
>>>>> Anthony Baxter <ant...@interlink.com.au> (AB) wrote:

>>>> Andrew Koenig wrote
>> The essence of Laura's argument, I think, is that many programmers
>> will not become familiary with if-expressions, as they're used only
>> from time to time, which means that when they do encounter them,
>> it will be a mental stumbling block.

AB> There's also the point that "if" already has existing meanings. This
AB> means that it's use in this rare form (at least, I'd hope it's rare;
AB> if a piece of code features a large number of ternary expressions,
AB> I'd question the sanity of the author), it will confuse the reader
AB> even more.

In all context is has a sim ilar meaning: that something depends on the
condition.
--
Piet van Oostrum <pi...@cs.uu.nl>
URL: http://www.cs.uu.nl/~piet [PGP]
Private email: P.van....@hccnet.nl

Andrew Koenig

unread,
Feb 8, 2003, 12:16:33 PM2/8/03
to
>> The essence of Laura's argument, I think, is that many programmers
>> will not become familiary with if-expressions, as they're used only
>> from time to time, which means that when they do encounter them,
>> it will be a mental stumbling block.

Alexander> I think you're missing one important aspect:
Alexander> predicatability of the sort that syntatic uniformity buys
Alexander> you. That is because it allows you to parse larger chunks,
Alexander> e.g. you always know that when you run into the token "if"
Alexander> the context is as follows: ``if CONDITION: DO_SOMETHING
Alexander> ...``.

This claim is already not true:

y = [i for i in x if i != 0]

Alexander> Introducing an "if" expression essentially means that both
Alexander> forms will be used interchangably for a certain percentage
Alexander> of cases, which I'd guess, makes *all* if expressions more
Alexander> difficult to read (at least once interchangable use of both
Alexander> forms has reached a certain frequency -- if that ever
Alexander> happens (as I think it has in Ruby and Perl, BTW)).

I doubt it, because statements aren't interchangeable with expressions
in general. If they were, there would be no need for a conditional
expression because we would already have one.

Try some examples and you'll see what I mean.

David Eppstein

unread,
Feb 8, 2003, 12:32:03 PM2/8/03
to
In article <yu99k7ga...@europa.research.att.com>,
Andrew Koenig <a...@research.att.com> wrote:

> EXAMPLE 6:
>
> p = (s == NULL)? str: s;
>
> This example sets a default value to substitute for s in case s is NULL.
> The likely rewrite:
>
> if (s == NULL)
> p = str;
> else
> p = s;

What's wrong with using s || str in this example?

--
David Eppstein UC Irvine Dept. of Information & Computer Science
epps...@ics.uci.edu http://www.ics.uci.edu/~eppstein/

Alex Martelli

unread,
Feb 8, 2003, 12:42:27 PM2/8/03
to
Andrew Koenig wrote:
...

> Once you do know what they mean, APL programs are often much easier
> to understand than their counterparts in other languages.

Having written many prototypes in APL (starting with my Thesis,
then when I was working in IBM Research), later recoding them
in Fortran, I must say that such understandability was not my
experience. I presumably did "know what they mean", as I had
originally coded them myself -- and as long as I was deeply
immersed in developing the APL code, my productivity with it
was quite good. But, going back to a working APL prototype I
had coded a few weeks or months earlier, and understanding it
thoroughly so I could code the Fortran equivalent, was NOT an
easy task at all for me.

That was 20 to 25 years ago, so maybe I was just being "too
clever by half" in the APL-coding part, to my detriment. But
I think the language does matter; partly because when I moved
to coding prototypes in REXX instead (before it acquired the
second 'X', when it was still an IBM internal-use thingy), I
found the "understanding and recoding" task much easier -- and
that wasn't due to the simple passage of time, because later
yet, having left IBM, when the "prototyping language" I would
typically use became Perl, then AGAIN "understanding and
recoding" became harder for me.

Funny enough, it seems I find prototype programs I coded in
languages typically considered to produce easily understandable
programs (such as REXX) to be easy to understand, while, when I
use for the same prototyping tasks languages typically considered
to produce hard-to-understand programs (such as APL or Perl),
then I have a harder time understanding my own code later...;-).


Alex


Roy Smith

unread,
Feb 8, 2003, 12:52:48 PM2/8/03
to
Andrew Koenig <a...@research.att.com> wrote:

> EXAMPLE 1:
>
> p = <null-terminated string>;
> q = <fixed-length char array>;
> for (i = 0; i < sizeof(<fixed-length char array>); i++) {
> if (*q++ != (*p? *p++: ' ')) {
> /* Complain that the two strings aren't equal */
> }
> }
>
> As a C programmer, I have no trouble understanding what's going on:
> The "if" compares the next character in q with either the next
> character in p or a blank, depending on whether we've hit the null
> that terminates p.

I find the above code horribly confusing. I've got 25 years of C coding
experience. I know I certainly wouldn't want to use anything I wrote 20
years ago as an example of how things should be done today :-)

One thing that's changed about how I write code today vs. 20 years ago
is that back then, I was a pure C hacker. I was head-down in C code 8
hours a day, 5 days a week. I could rattle off every nuance of every
minor language feature, and prided myself on writing the most compact
and efficient code that was possible. Today I use C, C++, Python, Perl,
TCL, shell, even a little Lisp, and I'm starting to look at Java.
Choice of language is often driven by external constraints. I no longer
have the luxury of knowing every nuance and idiom of every language I
use. This makes me tend to shy away from languages where you cannot be
effective without being a language lawyer.

OK, back to the code fragment in question.

I'm reading the condition of the if statement trying to understand it.
First I see *q++. OK, that's a common idiom and I instantly understand
that you're walking a pointer through an array. Then I see !=. OK,
you're comparing it to something. Then I see the conditional and have
to push my mental stack on level and say, "OK, let me understand what's
going on here, and then I'll come back and figure out how it fits with
what I've already understood". That's where it gets confusing. My
first pass at rewriting the for loop would be something like:

for (...) {
temp = *p ? *p++ : ' ';
if (*q++ != temp) {
/* complain */
}
}

which at least lets me understand the two things sequentially. First,
I'm getting a character from p (with blank-padding). Second, I'm
comparing that to a character from q. Next, I think I'd try to invent
some more descriptive variable names for p, q, and temp.

But, all I've done so far is nit-picked your style, and my version still
uses the conditional, which I'm arguing against. So, I think my next
step would be to rewrite it again, like this:

for (...) {
if (*q++ != getNextCharWithBlankPadding (&p)) {
/* complain */
}
}

char getNextCharWithBlankPadding (char **p)
{
if (**p)
return *p++;
else
return ' ';
}

Of course this is less efficient than it was before (assuming the
optimizer doesn't in-line the function call), but until somebody
convinces me with real profiling data, I'm much more interested in
readability than efficiency. The pointer-to-a-pointer doesn't rank very
high on my readability scale, but that's a problem forced on us by C,
which wouldn't show up in Python.

Fundamentally, you're doing a complex set of things. You're walking two
different arrays in parallel, comparing them element by element, and
performing blank-padding on one of them. Squashing all that into one
line certainly makes it more compact, but it doesn't make it easier to
understand.

> EXAMPLE 2:
>
> mode = cvlong(param, strlen(param), 8) |
> (c == 'c'? S_IFCHR: S_IFBLK);
>
> Before you flame at me for using an absolute constant here, I should
> point out that the "8" in the argument to cvlong means "Treat this
> as an octal number, please." Again, without ?: I might have written
>
> mode = cvlong(param, strlen(param), 8);
> if (c == 'c')
> mode |= S_IFCHR;
> else
> mode |= S_IFBLK;

You seem to thing this is a problem, but in my opinion, your code would
have been easier to understand written the second way.

> EXAMPLE 5:
> fprintf(file,
> *p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
> c);
> This code is a little sneaky.

I don't like sneaky code.

> Again, let's brush aside questions about what the code is trying to do,
> and consider how it does it. Without ?:, I think it would look like this:
>
> if (*p >= '0' && *p <= '7')
> fprintf(file, "\\%.3o", c);
> else
> fprintf(file, "\\%o", c);

Personally, I would have refactored this as something like:

format = "\\%o";


if (*p >= '0' && *p <= '7')

format = "\\%.3o";
fprintf (file, format, c);

and I think the result is better (easier to understand) than either of
your alternatives.

Dave Brueck

unread,
Feb 8, 2003, 1:58:51 PM2/8/03
to
On Sat, 8 Feb 2003, Laura Creighton wrote:

> Regardless of the reasons some people _want_ conditionals, people
> are going to _use_ them because they save keystrokes. The number of
> people who will do anything to save keystrokes is legion.

But how effectively can the language police peoples' laziness, and should
it? I mean, a lazy developer could just as easily save keystrokes by using
all one- and two-character variable names, indenting only a single space,
and not writing any comments. The same laziness (lack of discipline) will
manifest itself in a lack of architecture/design, lack of tests, etc.

Pair programming or code reviews or even some good old fashioned mentoring
is the place to encourage best practices and discourage bad ones - not the
language.

-Dave

Andrew Koenig

unread,
Feb 8, 2003, 1:13:56 PM2/8/03
to
David> In article <yu99k7ga...@europa.research.att.com>,

David> Andrew Koenig <a...@research.att.com> wrote:

>> EXAMPLE 6:
>>
>> p = (s == NULL)? str: s;
>>
>> This example sets a default value to substitute for s in case s is NULL.
>> The likely rewrite:
>>
>> if (s == NULL)
>> p = str;
>> else
>> p = s;

David> What's wrong with using s || str in this example?

Different semantics. Writing

p = s || str;

would be the same as

if (s == NULL) {
if (str == NULL)
p = 0;
else
p = 1;
}

which, in turn, wouldn't compile because you can't assign 1 to a pointer.

Perhaps you were thinking that "||" in C works like "or" in Python?

Laura Creighton

unread,
Feb 8, 2003, 12:56:54 PM2/8/03
to
> Laura> Because I am one. Because I know so many of them. Because
> Laura> 'lazy work' and 'hurried work' has been the bane of all
> Laura> creators since we first started creating anything.
>
> As an experiment, I went back through a 2800-line C program that I
> wrote about 20 years ago (!) to see how often I used ?: and in what
> context. I figure that reading my own 20-year-old code is probably a
> lot like reading some else's code.

The fact that you would not abuse a ternary does nothing to put my
mind at rest. I've wished for a world where all the C and C++
programs I have encountered have been written by you, many times,
sometimes in those exact words, as well.

If you don't think that it will be used very often, do you admit that it
might be on the side of 'diminishing returns', a feature request that
doesn't 'pull enough weight' to be worth doing? Or do you think that any
language feature that is of use to somebody should automatically go into
a language to 'add desired functionality'? I doubt very much that you
believe this, (though if you do, we have found the disagreement, for
certain). How do you determine if a proposed language feature 'pulls
enough weight'?

Laura

Tim Peters

unread,
Feb 8, 2003, 1:09:06 PM2/8/03
to
[Andrew Koenig]

> EXAMPLE 6:
>
> p = (s == NULL)? str: s;
>
> This example sets a default value to substitute for s in case s is NULL.
> The likely rewrite:
>
> if (s == NULL)
> p = str;
> else
> p = s;

[David Eppstein]


> What's wrong with using s || str in this example?

C's || isn't Python's "or". x || y in C is like Python's

(x and 1) or (y and 1) or 0

That is, || always returns a little bool-like integer in C, regardless of
its operands' types.


Laura Creighton

unread,
Feb 8, 2003, 1:13:55 PM2/8/03
to
> >> Once you do know what they mean, APL programs are often much easier
> >> to understand than their counterparts in other languages.
>
> Laura> Then you go away for six months and code in some other language.
> Laura> And then you get a bug report. You look at your code and no longer
> Laura> remember what the heck it was that you were trying to do.
>
> In 1976 I wrote an entire student-registration system in APL.
> In 1977, not having looked at the code for a year, I was brought
> back to make some major changes to it.
>
> I had no trouble understanding what the code was doing.

You never had that experience? That makes you the very first APL
programmer I have conversed with for whom that is true. I thought
that it was a universal. Happened to everybody ... at least once.

> >From that experience, I can say confidently that APL's lack of
> data-abstraction facilities is a much more bigger problem than its
> brevity.
>
> Incidentally, that code was still in use when I checked back 15 years
> later.

Unmodified? Maybe they were scared to touch it :-)

Laura

Andrew Koenig

unread,
Feb 8, 2003, 1:32:12 PM2/8/03
to
>> I had no trouble understanding what the code was doing.

Laura> You never had that experience? That makes you the very first APL
Laura> programmer I have conversed with for whom that is true. I thought
Laura> that it was a universal. Happened to everybody ... at least once.

I first learned APL in 1967, when I was in high school. I may well
have burned myself a few times early on, but by the time I wrote the
registration system, I had been programming in APL for nine years,
and had learned how to do it.

Andrew Koenig

unread,
Feb 8, 2003, 1:30:35 PM2/8/03
to
>> As an experiment, I went back through a 2800-line C program that I
>> wrote about 20 years ago (!) to see how often I used ?: and in what
>> context. I figure that reading my own 20-year-old code is probably a
>> lot like reading some else's code.

Laura> The fact that you would not abuse a ternary does nothing to put
Laura> my mind at rest. I've wished for a world where all the C and
Laura> C++ programs I have encountered have been written by you, many
Laura> times, sometimes in those exact words, as well.

I'm flattered :-)

Laura> If you don't think that it will be used very often, do you
Laura> admit that it might be on the side of 'diminishing returns', a
Laura> feature request that doesn't 'pull enough weight' to be worth
Laura> doing? Or do you think that any language feature that is of
Laura> use to somebody should automatically go into a language to 'add
Laura> desired functionality'? I doubt very much that you believe
Laura> this, (though if you do, we have found the disagreement, for
Laura> certain). How do you determine if a proposed language feature
Laura> 'pulls enough weight'?

One way is by considering the alternatives.

For example, suppose I want to write

z = max(x, y)

without calling a function. Yes, I know that's contrived, but examples
often are. I would like to be able to write

z = x if x > y else y

Today I can't. What can I do? I can write

if x > y:
z = x
else:
z = y

in which I must utter "z" twice and might get it wrong one of those times.
Or I can write

z = (x, y)[x <= y]

which evaluates x and y twice, and in which I must remember to invert
the condition. Or I can write

z = { True: x, False: y }[x > y]

which still evaluates x and y twice, and is getting wordy.

Or -- and this is what really convinces me -- I can write

z = x > y and x or y

which looks really cool, and is just plain wrong, because if x is
0 and y is negative, the result is 0 instead of y.

It's this last example that really horrifies me, because people see
it, think that "if a then b else c" should be written as
"a and b or c", and then get into trouble.

The fact that people are suggesting such circumlocutions says to me
that there is a use for the feature, and the fact that people are
suggesting incorrect circumlocutions makes my skin crawl.

Carlos Ribeiro

unread,
Feb 8, 2003, 1:47:42 PM2/8/03
to

While I agree that programmers love to save keystrokes, in some cases is more
important to save line count. Long blocks of code tend to be hard to read,
and in some cases, modularization with functions isn't possible or desirable.
If used **with care**, conditional operators may allow to eliminate a few
'auxiliary' if statements out of the way, thereby making the structure
clearer. Things such as selecting values for printing, etc., don't need to
stay on the way, and may be distracting when looking at the code.
Removing/simplifying them is a good reason to use the conditional operator.


Carlos Ribeiro
crib...@mail.inet.com.br

David Eppstein

unread,
Feb 8, 2003, 2:17:49 PM2/8/03
to
In article <mailman.1044727707...@python.org>,
Laura Creighton <l...@strakt.com> wrote:

> If you don't think that it will be used very often, do you admit that it
> might be on the side of 'diminishing returns', a feature request that
> doesn't 'pull enough weight' to be worth doing? Or do you think that any
> language feature that is of use to somebody should automatically go into
> a language to 'add desired functionality'? I doubt very much that you
> believe this, (though if you do, we have found the disagreement, for
> certain). How do you determine if a proposed language feature 'pulls
> enough weight'?

My impression is that a ternary operator is already used, often, in the
guise of "x and y or z" or "[z,y][x]" or still worse
"[lambda:y,lambda:z][not x]()". Introducing a true ternary operator
might at least prevent programmers from committing such atrocities.

But, programmers uninterested in writing readable code will anyway find
other ways to be unreadable. What I am not sure about is whether the
gain in readability of code compared to the existing ternary hacks is
enough to outweigh the increase in cognitive complexity of one more
language feature. I am pretty sure that GvR is better at judging that
kind of balance than I am, though, so I don't see a lot of point in
subjecting the matter to a vote.

Andrew Koenig

unread,
Feb 8, 2003, 2:23:53 PM2/8/03
to Tim Peters
Tim> C's || isn't Python's "or". x || y in C is like Python's

Tim> (x and 1) or (y and 1) or 0

Tim> That is, || always returns a little bool-like integer in C, regardless of
Tim> its operands' types.

...and in C++ it returns a bool.

Laura Creighton

unread,
Feb 8, 2003, 2:42:45 PM2/8/03
to
> On Sat, 8 Feb 2003, Laura Creighton wrote:
>
> > Regardless of the reasons some people _want_ conditionals, people
> > are going to _use_ them because they save keystrokes. The number of
> > people who will do anything to save keystrokes is legion.
>
> But how effectively can the language police peoples' laziness, and should
> it? I mean, a lazy developer could just as easily save keystrokes by using
> all one- and two-character variable names, indenting only a single space,
> and not writing any comments. The same laziness (lack of discipline) will
> manifest itself in a lack of architecture/design, lack of tests, etc.
>
> Pair programming or code reviews or even some good old fashioned mentoring
> is the place to encourage best practices and discourage bad ones - not the
> language.

I don't understand this reasoning. It means that the people who don't
have anybody to mentor them are stuck with _another_ uncorrected bad
practice, which they are probably unaware of, and people who are
mentoring have yet another thing to decide when to forbid and how to
teach. I've already got plenty of languages where this is the case.
I rather liked finding one where I got code I more or less liked
automatically, and which almost never made me wince.

Laura
>
> -Dave
>
> --
> http://mail.python.org/mailman/listinfo/python-list

Alexander Schmolck

unread,
Feb 8, 2003, 3:01:11 PM2/8/03
to
Andrew Koenig <a...@research.att.com> writes:

> Alexander> e.g. you always know that when you run into the token "if"
> Alexander> the context is as follows: ``if CONDITION: DO_SOMETHING
> Alexander> ...``.
>
> This claim is already not true:
>
> y = [i for i in x if i != 0]

True. Still, that doesn't mean that allowing further grammatically distinct
"if"s wouldn't have a negative impact. The list comprehension "if" at least is
quite limited: there is no "else" part, you can't chain it and it occurs in a
fairly narrow context. Introducing an "if" expression would almost certainly
increase the impact:

if ...:
y = [i if ... else ... for i if i ...] if ... else ...

>
> Alexander> Introducing an "if" expression essentially means that both
> Alexander> forms will be used interchangably for a certain percentage
> Alexander> of cases, which I'd guess, makes *all* if expressions more
> Alexander> difficult to read (at least once interchangable use of both
> Alexander> forms has reached a certain frequency -- if that ever
> Alexander> happens (as I think it has in Ruby and Perl, BTW)).
>
> I doubt it, because statements aren't interchangeable with expressions
> in general. If they were, there would be no need for a conditional
> expression because we would already have one.

Also true; I was quite sloppy above. In Ruby there is no if "statement",
because like in lisp, there is no statement/expression dichotomy. Thus there
are actually 3 ways to do exactly the same "ternary":


irb(main):002:0> a = :foo if true
:foo
irb(main):003:0> a = if true then :foo end
:foo
irb(main):003:0> a = true ? :foo : nil
:foo

(proving that just because there is no need for something doesn't mean nobody
would add it ;)

As there is a (increasingly blurred) distinction between expressions and
statements in python, the amount of spurious variation wouldn't be quite that
large (I do find that variation unpleasant when reading ruby code, BTW). Still

if a:
b = c()
else:
b = d()

and

b = c() if a else d()

are clearly interchangable in a slightly broader sense.


>
> Try some examples and you'll see what I mean.
>

Oh, I'm pretty sure I know what you mean.

But I think that Python, for the better or the worse, has decided to seperate
relatively sharply between expressions and statements. There are some
advantages (e.g. more uniform code and less deeply nested expressions) and some
disadvantages (e.g. a loss of conciseness).

And I do certainly appreciate the style that results from everything being an
expression in lisp.

However, I can see little point in making python feel more like lisp. If I
want lisp, I program in lisp, not something that has evolved into a bad copy
thereof carrying around the remains of its former character as a mere
encumbrance and mark of its inferiority (<cough>C++</cough>:).

Let's just not have conditionals or assignments in expressions, even if we
already have lambdas and list comprehensions.


alex

Erik Max Francis

unread,
Feb 8, 2003, 3:56:19 PM2/8/03
to
Alexander Schmolck wrote:

> True. Still, that doesn't mean that allowing further grammatically
> distinct
> "if"s wouldn't have a negative impact. The list comprehension "if" at
> least is
> quite limited: there is no "else" part, you can't chain it and it
> occurs in a
> fairly narrow context. Introducing an "if" expression would almost
> certainly
> increase the impact:
>
> if ...:
> y = [i if ... else ... for i if i ...] if ... else ...

Yep, language features can be abused. That's true for any language
feature in any language. I don't find it a compelling argument,
especially in this case where the construct being proposed has the
opportunity make code _more_ clear and concise when used properly, not
less.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, USA / 37 20 N 121 53 W / &tSftDotIotE
/ \ Lawyers, I suppose, were children once.
\__/ Charles Lamb
The laws list / http://www.alcyone.com/max/physics/laws/
Laws, rules, principles, effects, paradoxes, etc. in physics.

Andrew Dalke

unread,
Feb 8, 2003, 4:00:57 PM2/8/03
to
Ian Bicking:
> Why do you lack such faith in programmers? Python is the programmer's
> friend, not the programmer's keeper.

Experience?

I've been thinking about that viewpoint, since you first suggested it
to me. I don't think it's a lack of faith. Rather, it's the experience
that
the more flexible the language the more likely it is that dialect will
emerge that make it harder to share and understand others' code.

I have seen it happen in Perl. I have seen it happen in C++. I would
rather limit it from happening in Python.

As such, Python is the programming community's friend, and not
simply the single programmer's friend.

> What trauma you have suffered at
> the hands of inferior code should not blind you. Beautiful code is
> something that is nurtured and *created*.

There's something to be said for beauty. But beauty is not additive.
Bake a pie from the best ingredients using the best oven and you
can still end up with a mess. (Okay, that's a Heinlein reference :)

> You cannot attain it by
> forming an environment where all bad techniques are outlawed, based on
> the expectation that given no other options the bad programmer will
> somehow write beautiful code. It would be like taking a cast of a
> beautiful orchid, then seeding a ragweed beneath the cast. The ragweed
> will not become an orchid simply because no other shape is allowed it.

But you can make square watermelons that way. ;)

Orchids live in wild, wet, verdant jungles. Outside of their natural
environment, orchids live in carefully tended hothouses. (As I recall.
I am no horticulturalist.)

Taking your analogy futher, does that mean that everything should
be jumbled together (the jungle) so that orchid might live naturally?
Or that we should have highly controlled environment, so that they
might live elsewhere?

Laura does not propose that all bad techniques are outlawed. After
all, "a FORTRAN programmer can write FORTRAN code in
any language". She pointed out that people will use if/else expression
in the interest of saving a few keystrokes, rather than promote
readability, which should be a primary goal of good software
development.

And that's a view I enjoy as well. I've seen people do it, and
seen the confusion it sows.

> Ugly code can be thrown away. That task is easy. Our efforts should
> focus on the beautiful code, because that is the difficult task and the
> worthwhile task. We should judge this extension to the language with
> our aspiration, not with our fears.

Ugly code can not be thrown away if it is useful. It can be replaced.

There are some who will work and work to make the most elegant
code. There's the story of the programmer who will stare and stare
at code to make it smaller and tighter. "I am done not when I have
nothing to add but when I have nothing more to remove."

I am not one of those people. I am happy with my good-looking
code. It pays the job, and it doesn't have the upkeep like some
beautiful wom^H^H^Hcode have. And I get along better with
my clients and friends than rewriting everything for beauty.

Andrew
da...@dalkescientific.com


Andrew Dalke

unread,
Feb 8, 2003, 4:52:57 PM2/8/03
to
I'll apologize right now and say that yes, I know I'm looking at the
details and not at the, ahem, larger picture. Call me a pointalist ;)

But I do like examples, so thanks for doing this!

Andrew Koenig:


> EXAMPLE 1:
>
> p = <null-terminated string>;
> q = <fixed-length char array>;
> for (i = 0; i < sizeof(<fixed-length char array>); i++) {
> if (*q++ != (*p? *p++: ' ')) {
> /* Complain that the two strings aren't equal */
> }
> }

For this case in Python,

if q != p + " " * (q_size - len(p)):
complain

or if you don't like the !=, use "startswith". Or if you don't
like the " "*value use a format string

format_string = "%-" + len(q_size) + "s"
...
if not q.startswith(format_string % p)

or

if q[:len(p)] != p or q[len(p):q_size].strip() != "":
complain

> EXAMPLE 2:
>
> mode = cvlong(param, strlen(param), 8) |
> (c == 'c'? S_IFCHR: S_IFBLK);

Perhaps {"c": S_IFCHR}.get(c, S_IFBLK) ?

The dictionary approach has the advantage of being easily
extendable as well.

> EXAMPLE 3:
>
> fprintf(tf, "%c %#o %d %d ",
> mode == S_IFBLK? 'b': 'c',
> buf->st_mode & 07777,
> major(buf->st_rdev),
> minor(buf->st_rdev));

{S_IFBLK: "b"}.get(mode, "c")

> EXAMPLE 4:
>
> fprintf(stderr, "package %s\n",
> strcmp(pack->iname, instr)? pack->ename: instr);

This is a place where I would have done
if pack.iname == instr:
package_name = instr
else:
package_name = pack.ename
print >> sys.stderr, "package", package_name

I think even with an if/else expression, introducing the
variable name helps describe what's going on.

> EXAMPLE 5:
>
> fprintf(file,
> *p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
> c);

I assume in Python this would have a string 's' and an index 'i'.
What about
escape_codes = dict.from_keys("01234567", \\%.3o)
...
file.write(escape_codes.get(s[i:i+1], "\\%o") % c)

(The s[i:i+1] is in case i is past the end of string.)

> EXAMPLE 6:
>
> p = (s == NULL)? str: s;

Hmm. Let's assume that s can be None or a string. If it can't
be the empty string, then

p = s or str

If it can be the empty string, the above fails since "" is a
false value. In which case I would use

p = s
if p is None:
p is str

and not use the [s, str][s is None] hack.

> EXAMPLE 7:
>
> Here, hrout and mrout are functions:
>
> r = (*(line[0] == '.'? mrout: hrout)) (line, out);

Again, a table would work.

r = ( {".": mrout}.get(line[0][:1], hrout) )(line, out)

but I would more likely have done

if line[0][:1] == ".":
f = mrout
else:
f = hrout
f(line, out)

If there were more characters to dispatch on then the table
lookup could be extended to

r = ( {".": mrout,
"x": xrout,
"t": trout}.get(line[0][:1], hrout) )(line, out)

but if I did a few of these I would have had

class DefaultLookup:
def __init__(self, table, default):
self.table = table
self.default = default
def __getitem__(self, item):
return self.table.get(item, self.default)

and hidden the strangeish get(..., hrout) part behind a

dispatch_table = DefaultLookup({".": .... trout}, hrout)
...
r = dispatch_table[line[0][:1]](line, out)

> So... I used ?: 7 times in 2800 lines, or once every 400 lines or so.
>
>
> Looking back on these examples, I find that in the first five of them,
> the use of ?: makes the code easier to understand, even after 20 years.

For #1, I think a more Pythonic solution exists.

For #2, #3, and #7, I used a dict lookup, but in the simple binary
cases used I think tht approch is heavy handed and a ternary operator
would be clearer.

For #4, I think having the intermediate variable helps a bit
with explaining what is going on.

For #5, I think a table lookup is a bit better, again, partially
because it introduces a name.

For #6, depending on the situation, I think the 'x or y' operator
may get the solution. Otherwise I think the ternary operator is
clearer.

So I would suggest that only 4 of those scan better with a
ternary operator.

Andrew
da...@dalkescientific.com


Dave Brueck

unread,
Feb 8, 2003, 5:22:31 PM2/8/03
to
On Sat, 8 Feb 2003, Laura Creighton wrote:

> > On Sat, 8 Feb 2003, Laura Creighton wrote:
> >
> > > Regardless of the reasons some people _want_ conditionals, people
> > > are going to _use_ them because they save keystrokes. The number of
> > > people who will do anything to save keystrokes is legion.
> >
> > But how effectively can the language police peoples' laziness, and should
> > it? I mean, a lazy developer could just as easily save keystrokes by using
> > all one- and two-character variable names, indenting only a single space,
> > and not writing any comments. The same laziness (lack of discipline) will
> > manifest itself in a lack of architecture/design, lack of tests, etc.
> >
> > Pair programming or code reviews or even some good old fashioned mentoring
> > is the place to encourage best practices and discourage bad ones - not the
> > language.
>
> I don't understand this reasoning. It means that the people who don't
> have anybody to mentor them are stuck with _another_ uncorrected bad
> practice, which they are probably unaware of, and people who are
> mentoring have yet another thing to decide when to forbid and how to
> teach.

Wait, back up, maybe I just didn't express my opinion very well. I was
mostly responding to the assertion that people would use (abuse)
if-expressions because it would save keystrokes, and my opinion is that,
true or not (and I don't think it is), that's not a great reason for being
against adding them to the language. The lack of discipline that leads to
taking short cuts manifests itself in too many places for the language to
effectively prevent (hence the examples of lousy variable names and lack
of comments - *far* more damaging than if-expression abuse). Besides not
being a problem the language can solve, excluding features based on what
lazy programmers do seems to be catering to the wrong audience, so as a
guideline it should be used with caution.

Now, as to the notion that a lack of mentor means you're "stuck", I partly
_agree_, but I will point out that all the mentor you need may be a
chapter in a book or a web page that teaches some best practices for the
language.

Having said that, I will also readily admit that in my experience people
who code in isolation *do* tend to write lousier code than those who have
peers. The primary example I have of this is myself - I was self-taught
and had _no_ notion of code readability, maintainability, etc - instead of
"software development" it was "code slinging". Books like "Code Complete"
and "Writing Solid Code" were real eye openers for me, but the greatest
improvement to my coding style came when I worked with other people who
took the time to point out some bad habits I had and show me some better
ways to do things.

Finally, does adding this feature *really* add that much to what a mentor
or teacher has to do? IMO, not really. In an introductory course you
probably wouldn't even mention if-expressions at all, and goodness, most
people would be smart enough to figure it out on their own (a skill
they'll have to develop no matter what). A good teacher won't try to
hand-hold the student through every little feature anyway.

-Dave

Aahz

unread,
Feb 8, 2003, 5:32:54 PM2/8/03
to
In article <yu991y2i...@europa.research.att.com>,

Andrew Koenig <a...@research.att.com> wrote:
>
>Alexander> I think you're missing one important aspect:
>Alexander> predicatability of the sort that syntatic uniformity buys
>Alexander> you. That is because it allows you to parse larger chunks,
>Alexander> e.g. you always know that when you run into the token "if"
>Alexander> the context is as follows: ``if CONDITION: DO_SOMETHING
>Alexander> ...``.
>
>This claim is already not true:
>
> y = [i for i in x if i != 0]

That's a limited counter-claim because of the brackets setting off the
listcomp. It's also further limited by the functionality available to
the if.
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

Register for PyCon now! http://www.python.org/pycon/reg.html

Aahz

unread,
Feb 8, 2003, 5:36:46 PM2/8/03
to
In article <mailman.104472920...@python.org>,

Andrew Koenig <a...@research.att.com> wrote:
>
>Or -- and this is what really convinces me -- I can write
>
> z = x > y and x or y
>
>which looks really cool, and is just plain wrong, because if x is
>0 and y is negative, the result is 0 instead of y.
>
>It's this last example that really horrifies me, because people see
>it, think that "if a then b else c" should be written as
>"a and b or c", and then get into trouble.
>
>The fact that people are suggesting such circumlocutions says to me
>that there is a use for the feature, and the fact that people are
>suggesting incorrect circumlocutions makes my skin crawl.

Then attack the problem at its roots: flame the people who suggest such
circumlocutions instead of more Pythonic idioms. </half-joking>

Jp Calderone

unread,
Feb 8, 2003, 5:31:09 PM2/8/03
to
On Sat, Feb 08, 2003 at 04:50:06PM +0000, Alexander Schmolck wrote:
> Andrew Koenig <a...@research.att.com> writes:
> > As I noted (with variations) in another thread, I think that
> > readibility is proportional to brevity * familiarity.
>
> Almost, but not quite (see below).
>
> > So something short but unfamiliar is hard to read until you
> > become familiar with it.

> >
> > The essence of Laura's argument, I think, is that many programmers
> > will not become familiary with if-expressions, as they're used only
> > from time to time, which means that when they do encounter them,
> > it will be a mental stumbling block.
>
> I think you're missing one important aspect: predicatability of the sort that
> syntatic uniformity buys you. That is because it allows you to parse larger
> chunks, e.g. you always know that when you run into the token "if" the context
> is as follows: ``if CONDITION: DO_SOMETHING ...``.

To expand on this, a bit...

Let's refer back to the PEP itself:

x if C else y if D else z <==> x if C else (y if D else z)
x or y if C else z <==> (x or y) if C else z
x if C else y or z <==> x if C else (y or z)
lambda: x if C else y <==> lambda: (x if C else y)
x if C else lambda: y <==> SyntaxError
x if C else y, z <==> (x if C else y), z
x, y if C else z <==> x, (y if C else z)

Now, to me, at least 3 of the above forms will be confusing, until I
memorize the new precedence relationships this introduces. Any time I read
one of these unfamiliar groupings, I'm going to have to stop and find the
documentation that describes this. There is no predicability here - the
precedence has been arbitrary decided. In isolation this form may be
perfectly readable, but does anyone really expect that isolation is going to
be the most common use?

Jp

--
It is practically impossible to teach good programming style to
students that have had prior exposure to BASIC: as potential
programmers they are mentally mutilated beyond hope of
regeneration. -- Dijkstra
--
up 2:28, 5 users, load average: 0.39, 0.30, 0.23

Andrew Dalke

unread,
Feb 8, 2003, 7:10:23 PM2/8/03
to
Andrew Koenig:

> As an experiment, I went back through a 2800-line C program that I
> wrote about 20 years ago (!) to see how often I used ?: and in what
> context. I figure that reading my own 20-year-old code is probably a
> lot like reading some else's code.

Ahh, but you're a good programmer, even 20 years ago.

I analyzed a codebase I have access to. It's proprietary, so
I can't say much of the details, but can say it was written over
a course of years by life science researchers (chemists and
biologists) with little formal training in C++. There were a
couple of programmers, but with only a couple of years of
professional experience.

EXAMPLE 1: (used 6 times)

return arr[i] & mask ? true : false;

(This C++ code was pre any sort of built-in boolean so
those two values are defined as part of their package, I think.)

Python now has a bool, so this could be written as
return bool(arr[i] & mask)

If C++ does have a boolean type, then this isn't needed. Even
when it didn't exist, it would have been easy to define, just like
the values 'true' and 'false' were defined.

EXAMPLE 2:

ClassSpam::ClassSpam(huge, set, of, args, including, input_arg)
: instance_var(input_arg ? 2 : 1)
{
... rest of constructor ...
}

One way to do this in Python (besides an if/then statement) is
self.instance_var = bool(input_arg) + 1

This is needed for C++ because it's the only way to initialize
const values. It isn't needed in Python.

EXAMPLE 3: (used twice, 3x each)

sprintf(outputStr, msgFmt, is_clean ? "CLEAN": "DIRTY",
"spam=", spamStr,
output_format == FORMAT1 ? "STR1" : "STR2",
output_format == FORMAT1 ? "" : output_filename

consider instead

if self.output_format == FORMAT1:
output_info = ("STR1", "")
else:
output_info == ("STR2", output_filename)
print >> outputStr, msgFmt % ((["DIRTY", "CLEAN"][self.is_clean],
"spam=", self.spamStr) + output_info)

For C++ this is useful. Doesn't mean it's needed for Python.

EXAMPLE 4:

Personally, I can't figure out what this code is doing. Not because
of the ?:, which looks like

if (!ExportAs(inputStr, usingChemDraw ? CHEMDRAW : outputFilename))

but because the surrounding code is complicated. The above is used twice,
once in two different branches. I would have made it into a variable like

destination = usingChemDraw ? CHEMDRAW : outputFilename;
..
if (branch1) {
... ExportAs(inputStr, destination)
} else {
... ConvertFile(inputFilename, destination);
}

so I think the two uses of '?:' should have been folded into one.

EXAMPLE 5:

// flg can be 0 ("no"), 1 ("same as last time"), or 2 ("yes")
if (flg != 1 && cursor && (cursor.dirty() ? !flg : flg) {
delete cursor;
cursor = NULL;
}
if (!cursor) ...


I have a hard time following the logic. I think it's more
understandable as

if ((flg == 0 and cursor and cursor.dirty()) or
(flg == 2 and cursor and not cursor.dirty()):

assuming I didn't get something swapped.

EXAMPLE 6:

Okay, this is going through an array-like object indexed
by opaque handles and deleting terms which don't meet
a given criteria ... I think. The handles are never 0.

for (prev = 0, curr = arrary.first();
curr; curr = (prev ? array.first() : array.after(prev)) {
...
if (need_to_delete) {
array.remove(curr)
} else {
prev = curr;
}

This is just wrong. IMHO of course.

EXAMPLE 7
n = num_items == 2 ? 1 : (num_items == 3 ? 3 : 6);

In Python, perhaps better written as

n = {2: 1, 3: 3}.get(num_items, 6)

In C++ ... hey, it's got a switch statement, right?
switch (num_items) {
2: n = 1; break;
3: n = 2; break;
default: n=6; break;
}

Naah, pretty long. I'll let you all decide if it's needed for
C++. For Python, I'm willing to live with the dict solution.

EXAMPLE 8

n = arr == NULL ? 0 : arr->maxVal();

All nice and pretty, right? Well, two lines down is

head = arr->GetNext(index, n)

so there's no need to check that arr == NULL .. because
if it was NULL, the other code would break!

EXAMPLE 9

return ((minSize == maxval) ? 0 : minSize)

This term is looking for all items in a list which match a given
condition. If there are no items in the list with that condition, it
returns 0. The search is done by setting minSize to maxint
and doing the old
if condition_met:
minSize = min(currSize, minSize)

None of the items will ever have maxint, so at the end there's
the check to see if it's maxint and, if so, return 0.

I don't like the way the search is done. Still, if done that way
I would rather do something with a bit more comments

if minSize == maxval: # Nothing was found so return 0
return 0
return minSize

EXAMPLE 10 (4 cases)

return needs_inversion ? 1 - val : val;

Here are two options

if needs_inversion:
return -val
return val

Or perhaps the 'needs_inversion' should be a +1/-1 value
(inversion_factor) instead of a boolean flag, so the written as

return inversion_factor * val

I can't tell because the flag is used/set somewhere outside of the
scope of the file I'm looking at.

As mentioned, this is used 4 times. Twice as "1-val" and
once as "1.0 - val". So I think that a flag is the wrong approach
and this should use an inversion factor instead.

EXAMPLE 11:

In this case, atoms are being rearranged. Bonds can be in
either CIS or TRANS state. If the order of the atoms is the
same, keep them in the same state. Otherwise, invert them

if (need_inversion) {
bond -> setChirality(bond.chirality() == CIS ? TRANS : CIS);
}

I would rather there be a "bond.invertChirality()". Even then,
the internal implementation needs to swap. One way would be
to have the states be 0 and 1, so it could be done with 1-old_state.

I'm somewhat okay with how it's written now

EXAMPLE 12:

size = fromSize <= toSize ? fromSize : toSize

which in Python is

size = min(fromSize, toSize)

I think C++ has a 'min' as well?

EXAMPLE 13:

n = obj == NULL ? 0 : obj->getCount();
vector.append(n);
if (n > 0) {
arr -> append(something_else);
}

I would rather this be

if (obj == NULL) {
n = obj->getCount();
vector.append(n);
if (n > 0) { // Actually, I think n can never be 0 so this isn't needed
arr -> append(something_else);
}
} else {
vector.append(0);
}

Yes, it's more verbose (7 (or 6) coding lines vs. 4) but then I don't
have to track in my head the fact that n means 'no object or empty
object'.

EXAMPLE 14:

box = (n > 2) ? (n-2) * BOX_FACTOR : BOX_FACTOR;

How about
box = max(0, (n-2) * BOX_FACTOR)
?

And C++ has a max, right?

EXAMPLE 15:
if (pn != NULL) {
*pn = (rep_count < max_count ? rep_count + 1 : max_count);
}
which can be replaced by 'min' in Python and C++.

EXAMPLE 16: (4 in a row)

index_A = (atom1 != OXYGEN)
? long_function_name(atom1->realAtom(),
atomCount, reactionMapping)
: -1;
index_B = (atom2 != OXYGEN) ? function(atom2->realAtom(), params) : -1;
index_C = (atom3 != OXYGEN) ? function(atom3->realAtom(), params) : -1;
index_D = (atom4 != OXYGEN) ? function(atom4->realAtom(), params) : -1;

Yes, the real code is 4 lines long per assignment, so 16 lines total.
And it's hard to tell that all the parameters (unless noted otherwise)
are the same.

In C++ I would make a helper function.

In Python this could be done better by building a list, as in
the following 7 lines

indicies = []
for atom in (atom1, atom2, atom3, atom4):
if atom != OXYGEN:
indicies.append(long_function_name(atom.realAtom(),
atomCount,
reactionMapping))
else:
indicies.append(-1)

However, some might argue that a list comprehension and an
if/else expression is better.

indicies = [(long_function_name(atom->realAtom(), atomCount,
reactionMapping)
if atom != OXYGEN else -1)
for atom in (atom1, atom2, atom3, atom4)]

I don't think so.

This is a case where list comprehensions and if/else expression
would only serve to make things *less* readable, because the function
call is so heineously long that the small '[]' and 'if/else' symbols get
hidden amoung the noise.

Yet I am sure there are those who would use it, because writing
this in the first place is easy. Add if/else expressions and you enable
those people to write junk.

Okay, that's 42,500 lines of C++ code. There's a lot more, but
this analysis takes time. And in that code, there are (assuming I
counted correctly) 34 uses of ?:. making one use every 1,250 lines.

Andrew K found it was used once every 400 lines. The difference
may reflect that he's a good C programmer, and a professional
developer, while the code I'm reviewing was written by a buch of
non-software engineers.

Of those, I think 9 are appropriate for the C++ code and
4, count 'em, 4! might be justified for Python.

Assume that Python code is 5x more expressive than C++.
That means I think it might be used once every 2,000 lines
of Python code, amoung my target audience.

Assuming it saves 4 lines of code, then overall the code is
reduced in size by 0.04%.

Yet were people to introduce this ternary operator to Python,
it opens the way up for all sorts of nasties, like people doing

val = x if x < y else y
instead of
val = min(x, y)

This will happen. I have seen people post to this list asking
for the ternary operator exactly so they can write x<y?x:y.
We told them about min, max, and it made that request go away.

It also opens up Python code to allow the above listcomp + if/else

indicies = [(long_function_name(atom->realAtom(), atomCount,
reactionMapping)
if atom != OXYGEN else -1)
for atom in (atom1, atom2, atom3, atom4)]

I can guarantee you that people will write in this form. I do not
want to read nor maintain code written like this, but it will happen.

So from my sampling, I believe that people will misuse the ?: operator
about as often as it is used correctly, based on my analysis of a
non-trivial
amount of C++ code. The abuse will come from 1) not using the operator
correctly (eg, having a check which isn't needed, lke #8), 2) using it
to introduce confusing control flow (#13), 3) use it to make a hard-to-
understand expression (#5, #6), 4) use it in lieu of existing functions
(the various min/max examples, esp. #14, and also #7) and 5)
because it is 'cool' (#16, in the list comprehension).

Anyone else have numbers to suggest otherwise for my target
audience of "people who don't consider themselves to be software
engineers but still program"?

Andrew
da...@dalkescientific.com


Paul Rubin

unread,
Feb 8, 2003, 7:50:15 PM2/8/03
to
"Andrew Dalke" <ada...@mindspring.com> writes:
> EXAMPLE 1: (used 6 times)
>
> return arr[i] & mask ? true : false;
>
> (This C++ code was pre any sort of built-in boolean so
> those two values are defined as part of their package, I think.)
>
> Python now has a bool, so this could be written as
> return bool(arr[i] & mask)
>
> If C++ does have a boolean type, then this isn't needed. Even
> when it didn't exist, it would have been easy to define, just like
> the values 'true' and 'false' were defined.

It's still needed, if you want the function to return true or
false, instead of considering 0x1 and 0x04000 to be equivalent.

> EXAMPLE 2:
>
> ClassSpam::ClassSpam(huge, set, of, args, including, input_arg)
> : instance_var(input_arg ? 2 : 1)
> {
> ... rest of constructor ...
> }
>
> One way to do this in Python (besides an if/then statement) is
> self.instance_var = bool(input_arg) + 1
>
> This is needed for C++ because it's the only way to initialize
> const values. It isn't needed in Python.

I don't understand this one so I'll skip it.

> EXAMPLE 3: (used twice, 3x each)
>
> sprintf(outputStr, msgFmt, is_clean ? "CLEAN": "DIRTY",
> "spam=", spamStr,
> output_format == FORMAT1 ? "STR1" : "STR2",
> output_format == FORMAT1 ? "" : output_filename
>
> consider instead
>
> if self.output_format == FORMAT1:
> output_info = ("STR1", "")
> else:
> output_info == ("STR2", output_filename)
> print >> outputStr, msgFmt % ((["DIRTY", "CLEAN"][self.is_clean],
> "spam=", self.spamStr) + output_info)
>
> For C++ this is useful. Doesn't mean it's needed for Python.

Doing it the same way in Python looks ok to me.

> EXAMPLE 4:
>
> Personally, I can't figure out what this code is doing. Not because
> of the ?:, which looks like
>
> if (!ExportAs(inputStr, usingChemDraw ? CHEMDRAW : outputFilename))
>
> but because the surrounding code is complicated.

In that case the ?: isn't the issue.


>
> EXAMPLE 5:
>
> // flg can be 0 ("no"), 1 ("same as last time"), or 2 ("yes")
> if (flg != 1 && cursor && (cursor.dirty() ? !flg : flg) {
> delete cursor;
> cursor = NULL;
> }
> if (!cursor) ...
>
>
> I have a hard time following the logic. I think it's more
> understandable as
>
> if ((flg == 0 and cursor and cursor.dirty()) or
> (flg == 2 and cursor and not cursor.dirty()):
>
> assuming I didn't get something swapped.

Yes, it's clearer as originally written, no problem with getting
something swapped.

And so on for other examples...

Paul Paterson

unread,
Feb 8, 2003, 8:14:05 PM2/8/03
to

"Andrew Dalke" <ada...@mindspring.com> wrote in message
news:b2462f$qbs$1...@slb0.atl.mindspring.net...
> Andrew Koenig:
> > As an experiment, ... <snip experiment to look at examples of
conditional operator>

Continuing the experiment to look at Python code which might use the
conditional operator....

If my regular expression is correct (and it probably isn't because I'm not
good at them), then a quick scan of my Python22 directory structure finds
769 matches of the following pattern,

if <something>
variable = <some value>
else:
variable = <some other value>

in 381209 lines of code. I leave the statistical interpretation to the
reader.

I intend to post each one with an analysis of why they would or wouldn't be
better done using the new form....

In the meantime, here's the code - you can make up your own mind.

import re, sys, os

pattern =
re.compile(r"(^\s*if.*?$\s+(?P<var>\S+?)\s*=.*?$\s*else:\s+(?P=var).*?$)",
re.MULTILINE)


total_lines = 0
total_matches = 0

def look(arg, d, names):
"""Look in directory d for examples of conditional expressions"""
for filename in names:
if os.path.splitext(filename)[1] in (".py", ".pyw"):
text = open(os.path.join(d, filename)).read()
try:
matches = pattern.findall(text)
except:
print "%s::%s - failed!" % (d, filename)
else:
global total_matches, total_lines
total_matches += len(matches)
total_lines += len(text.splitlines())
print "%s::%s - %d (%d/%d)" % (d, filename, len(matches),
total_matches, total_lines)
for match in matches:
print "\n\n%s\n\n" % match[0]

path = os.path.join(sys.exec_prefix, "lib")
os.path.walk(path, look, None)


Paul Moore

unread,
Feb 9, 2003, 11:15:26 AM2/9/03
to
aa...@pythoncraft.com (Aahz) writes:

> In article <mailman.104472920...@python.org>,
> Andrew Koenig <a...@research.att.com> wrote:
>>
>>Or -- and this is what really convinces me -- I can write
>>
>> z = x > y and x or y
>>
>>which looks really cool, and is just plain wrong, because if x is
>>0 and y is negative, the result is 0 instead of y.

Of course, the result is y, instead of 0, really. max(0, -1) should be
0, yes?

>>It's this last example that really horrifies me, because people see
>>it, think that "if a then b else c" should be written as
>>"a and b or c", and then get into trouble.

People who think like that should be corrected (or shot :-))

>>The fact that people are suggesting such circumlocutions says to me
>>that there is a use for the feature, and the fact that people are
>>suggesting incorrect circumlocutions makes my skin crawl.
>
> Then attack the problem at its roots: flame the people who suggest such
> circumlocutions instead of more Pythonic idioms. </half-joking>

I'm not actually sure that's even half a joke. I only suggest things
like and/or or index-by-bool, to people who have already demonstrated
that they aren't willing to listen to suggestions that they look at
the problem in a wider context than just translating a single
conditional expression from the C idiom. I shouldn't give up that
easily, though - it dilutes my real point.

I haven't the patience to be a good teacher.

Paul.
--
This signature intentionally left blank

Martin Maney

unread,
Feb 9, 2003, 11:00:58 PM2/9/03
to
Andrew Koenig <a...@research.att.com> wrote:
> The fact that people are suggesting such circumlocutions says to me
> that there is a use for the feature, and the fact that people are
> suggesting incorrect circumlocutions makes my skin crawl.

+1

Martin Maney

unread,
Feb 9, 2003, 11:11:33 PM2/9/03
to
David Eppstein <epps...@ics.uci.edu> wrote:
> My impression is that a ternary operator is already used, often, in the
> guise of "x and y or z" or "[z,y][x]" or still worse
> "[lambda:y,lambda:z][not x]()". Introducing a true ternary operator
> might at least prevent programmers from committing such atrocities.

Better yet, it would save otherwise kind and compassionate people from
having to foist these wretches off on the innocent who come to them for
assistance.

> But, programmers uninterested in writing readable code will anyway find
> other ways to be unreadable. What I am not sure about is whether the
> gain in readability of code compared to the existing ternary hacks is
> enough to outweigh the increase in cognitive complexity of one more
> language feature.

But it's not an increase - it's a decrease. You get one form that is
both safe and IMO far more readable in place of at least three
different ugly, sometimes unsafe, hacks. (three has to be a lower
bound, since we've seen at least one significant variation on all of
the above just in recent pre-308 discussion)

Paul Paterson

unread,
Feb 9, 2003, 11:48:28 PM2/9/03
to
"Dennis Lee Bieber" <wlf...@ix.netcom.com> wrote in message
news:bqbjh-...@beastie.ix.netcom.com...
> Paul Paterson fed this fish to the penguins on Saturday 08 February
> 2003 05:14 pm:

>
>
> > structure finds 769 matches of the following pattern,
> >
> > if <something>
> > variable = <some value>
> > else:
> > variable = <some other value>
> >
> > in 381209 lines of code. I leave the statistical interpretation to the
> > reader.
>
> Ah, but perhaps the better statistic would be to also know how
many
>
> if <something>:
> <do something>
> else:
> <do something else>
>
> appear in the code referenced. If your 769 were 50% of the total
> if/else code, then a conditional might be a desirable feature. If 5%
> then it probably doesn't gain enough to justify adding it.
>
> Oh, what about?
>
> if <s1>:
> v = <some1>
> elif <s2>:
> v = <some2>
> elif...
> v = <some...>
> else:
> v = <else>
>
> A conditional expression won't replace that (though unless each
> <somen> is a dynamic equation, a list/dictionary look-up is likely
> better).
>

A minor modification could find both these statistics. The code is here,

http://groups.google.com/groups?selm=xXh1a.5331%24yn1.486089%40twister.austi
n.rr.com&oe=UTF-8&output=gplain


Andrew Koenig

unread,
Feb 9, 2003, 11:53:25 PM2/9/03
to
Martin> David Eppstein <epps...@ics.uci.edu> wrote:

>> My impression is that a ternary operator is already used, often, in the
>> guise of "x and y or z" or "[z,y][x]" or still worse
>> "[lambda:y,lambda:z][not x]()". Introducing a true ternary operator
>> might at least prevent programmers from committing such atrocities.

Martin> Better yet, it would save otherwise kind and compassionate
Martin> people from having to foist these wretches off on the innocent
Martin> who come to them for assistance.

LOL!!

>> But, programmers uninterested in writing readable code will anyway
>> find other ways to be unreadable. What I am not sure about is
>> whether the gain in readability of code compared to the existing
>> ternary hacks is enough to outweigh the increase in cognitive
>> complexity of one more language feature.

Martin> But it's not an increase - it's a decrease. You get one form
Martin> that is both safe and IMO far more readable in place of at
Martin> least three different ugly, sometimes unsafe, hacks. (three
Martin> has to be a lower bound, since we've seen at least one
Martin> significant variation on all of the above just in recent
Martin> pre-308 discussion)

+1.

Andrew Dalke

unread,
Feb 10, 2003, 1:54:37 AM2/10/03
to
Paul Rubin on my analysis of some C++ code's use of "?:" :

> > EXAMPLE 1: (used 6 times)
> >
> > return arr[i] & mask ? true : false;

> > If C++ does have a boolean type, then this isn't needed. Even


> > when it didn't exist, it would have been easy to define, just like
> > the values 'true' and 'false' were defined.
>
> It's still needed, if you want the function to return true or
> false, instead of considering 0x1 and 0x04000 to be equivalent.

If C++ has a bool type and builtin true/false then the C++ code
quoted above can be replaced with

return bool(arr[i] & mask)

If it doesn't have a bool type, then the project's standard
library, which includes a 'true' and a 'false' should include a
function like (excuse my rusty C++ experience)

template<T>
int bool(const T& cond) {
if (cond) {
return true;
} else {
return false;
}
}

In either case, the "?:" should not be needed for this example.

> > EXAMPLE 2:

> I don't understand this one so I'll skip it.

It's very C++ specific and its requirement doesn't easily translate
to Python. Briefly, C++ classes can have constant instance attributes.
Because they are constant, they can never be changed over the
lifespan of the instance. But they must be initialized somehow.
C++ has a syntax to initialize these attributes before the constructor
is actually called, but it requires that only an expression be evaluated.
Hence the use if ?:.

> > EXAMPLE 3: (used twice, 3x each)
> >
> > sprintf(outputStr, msgFmt, is_clean ? "CLEAN": "DIRTY",
> > "spam=", spamStr,
> > output_format == FORMAT1 ? "STR1" : "STR2",
> > output_format == FORMAT1 ? "" : output_filename

> > For C++ this is useful. Doesn't mean it's needed for Python.
>
> Doing it the same way in Python looks ok to me.

It's true. However, I thought the original C++ code was going to
print the format name and the destination, and so would use
output_format == ...
output_destination == ...

Instead, the format name and destination are both encoded in
the same variable. I had to look very closely to see that
this example wasn't a typo. Whereas in my alternate expression
as

if output_format == FORMAT1:
extra_args = ("STR1", "")
else:
extra_args = ("STR2", output_filename)
print outputStr % ( (args, ...) + extra_args)

it's easy to see that both args are meant to be set by the
same option.

> > EXAMPLE 5:
> >
> > // flg can be 0 ("no"), 1 ("same as last time"), or 2 ("yes")
> > if (flg != 1 && cursor && (cursor.dirty() ? !flg : flg) {
> > delete cursor;
> > cursor = NULL;
> > }
> > if (!cursor) ...
> >
> >
> > I have a hard time following the logic. I think it's more
> > understandable as
> >
> > if ((flg == 0 and cursor and cursor.dirty()) or
> > (flg == 2 and cursor and not cursor.dirty()):
> >
> > assuming I didn't get something swapped.
>
> Yes, it's clearer as originally written, no problem with getting
> something swapped.

I don't understand this. You say the original expression is
clearer than my rewrite? Or should that "as" be a "than"?

> And so on for other examples...

And so on? So the original C++ uses were better than the
alternative versions I suggested were Python available?

Andrew
da...@dalkescientific.com


Paul Rubin

unread,
Feb 10, 2003, 1:56:37 AM2/10/03
to
"Andrew Dalke" <ada...@mindspring.com> writes:
> > Yes, it's clearer as originally written, no problem with getting
> > something swapped.
>
> I don't understand this. You say the original expression is
> clearer than my rewrite? Or should that "as" be a "than"?

I mean the original version was clearer than the rewrite.

> > And so on for other examples...
>
> And so on? So the original C++ uses were better than the
> alternative versions I suggested were Python available?

If I remember correctly, I liked the original versions better than the
rewrites in most but not all of the examples. I didn't feel like
going on analyzing every last one of them.

Andrew Dalke

unread,
Feb 10, 2003, 2:04:03 AM2/10/03
to
Paul Paterson:

> Continuing the experiment to look at Python code which might use the
> conditional operator....
>
> If my regular expression is correct (and it probably isn't because I'm not
> good at them), then a quick scan of my Python22 directory structure finds
> 769 matches of the following pattern,
>
> if <something>
> variable = <some value>
> else:
> variable = <some other value>
>
> in 381209 lines of code. I leave the statistical interpretation to the
> reader.

You should also look at C/C++ code and see how many times C
code uses

if (something) {
variable = some_value;
} else {
variable = some_other_value;
}

For example, why doesn't this code from CPython's mmapmodule.c
use the ?: operator?

if (!map_size) {
m_obj->size = GetFileSize (fh, NULL);
} else {
m_obj->size = map_size;
}

After all, C has ?: so isn't this more readable, (insert pro-PEP arguments
here) as

m_obj->size = !map_size ? GetFileSize(fh, NULL) : map_size;
?

> I intend to post each one with an analysis of why they would or wouldn't
be
> better done using the new form....

Please also include the same analysis for Python's C implementation. ;)

Andrew
da...@dalkescientific.com


Andrew Dalke

unread,
Feb 10, 2003, 2:05:40 AM2/10/03
to
Paul Paterson

> Continuing the experiment to look at Python code which might use the
> conditional operator....
>
> If my regular expression is correct (and it probably isn't because I'm not
> good at them), then a quick scan of my Python22 directory structure finds
> 769 matches of the following pattern,
>
> if <something>
> variable = <some value>
> else:
> variable = <some other value>

Oh yeah, and you should also check for

if <something>:
return <some value>
return <some other value>

Which is also more common in C code compared to

return something ? some_value : some_other_value;

Andrew
da...@dalkescientific.com


Paul Paterson

unread,
Feb 10, 2003, 2:35:31 AM2/10/03
to
"Andrew Dalke" <ada...@mindspring.com> wrote in message
news:b27ip8$d7c$1...@slb4.atl.mindspring.net...

Wait, I thought that conditional expressions were supposed to reduce the
cognitive overload and now here I am having to think up weird and wonderful
regular expressions to try to evaluate the cognitive benefit... <wink>


Andrew Dalke

unread,
Feb 10, 2003, 3:06:57 AM2/10/03
to
Martin Maney:

> Better yet, it would save otherwise kind and compassionate people from
> having to foist these wretches off on the innocent who come to them for
> assistance.

Foist? The FAQ specifically says "in many cases .. but there's a flaw"
"ugly", "rare", and "as a last resort".


> But it's not an increase - it's a decrease. You get one form that is
> both safe and IMO far more readable in place of at least three
> different ugly, sometimes unsafe, hacks. (three has to be a lower
> bound, since we've seen at least one significant variation on all of
> the above just in recent pre-308 discussion)

I've been trying to show that people will be prone to use the ternary
if/else expression even when a better (by my definition of better)
option is available. Three such are

return True if x else False
instead of
return bool(x)

c=chr(48+i if i<10 else 55+i)
instead of
c = "0123456789ABCDEF"[i]
OR
c = hex(i)[2:]

a = x if x < y else y
instead of
a = min(x, y)

Not only that, but I've been trying to show that the
misuse rate, based on analysis of existing C/C++ code,
is roughly comparable to the proper use rate.

(A true Python expert is unlikely to misuse if/else
when a better Python solution exists, but I tried to factor
in that most of the user base is not an expert.)

Andrew
da...@dalkescientific.com


Andrew Dalke

unread,
Feb 10, 2003, 3:21:24 AM2/10/03
to
Andrew Koenig:

> The fact that people are suggesting such circumlocutions says to me
> that there is a use for the feature, and the fact that people are
> suggesting incorrect circumlocutions makes my skin crawl.

Hey, you also caught when I used the if/else expression the wrong way ;)

In the universe of all times when if/else might be used, it is relatively
uncommon to actually use it (by my analysis of C/C++ code). Of those
C/C++ examples, some of the cases had better "Pythonic" examples.
I've given some examples elsewhere.

My estimate is that for every appropriate use of ?: in C/C++ only
1 of those 3 is appropriate for Python because other solutions exist
which *are* natural, and not circumlocutions. Of those, my estimate
is that about 1/2 of the time someone will choose to use the ?: way
instead of the clearer non-?: solution.

This means that for every two uses of ?: in Python-with-if/else-expressions
which ARE understandable, readable, etc, there will be one to
two cases which ARE NOT.

And that makes my skin crawl.

I prefer no circumlocutions ("use an if/else statement") and no
crawling skin.

Andrew
da...@dalkescientific.com


Andrew Dalke

unread,
Feb 10, 2003, 3:31:28 AM2/10/03
to
Paul Rubin:

> I mean the original version was clearer than the rewrite.

Ahh, okay. Just to make it clear, you find

if (flg != 1 && cursor && (cursor.dirty() ? !flg : flg) {

more clear than

if ((flg == 0 and cursor and cursor.dirty()) or
(flg == 2 and cursor and not cursor.dirty()):

I had to scan the first one several times to understand what
it was doing. In the real code, the description of the
states flg can take was about 20 lines previous.

> If I remember correctly, I liked the original versions better than the
> rewrites in most but not all of the examples. I didn't feel like
> going on analyzing every last one of them.

Some of the examples were appropriate but unneeded, like


n = arr == NULL ? 0 : arr->maxVal();

// skip a couple lines, then


head = arr->GetNext(index, n)

where if arr is NULL then the other function would cause a
dereference through NULL.

Others were correct but they data structure used was incorrect.
For example,
return if_invert ? -val : val;
uses a boolean flag for "if_invert" but could/should use an
inversion factor, as in
return inversion_factor * val

So yes, the use was appropriate for several of these, in
the limited scope of what it does, but in the larger scale
it was simplifying code which was actually overly complicated
for other reasons. It was hiding poor construction.

Andrew
da...@dalkescientific.com


Andrew Dalke

unread,
Feb 10, 2003, 3:35:25 AM2/10/03
to
Me:

> > Oh yeah, and you should also check for
> > if <something>:
> > return <some value>
> > return <some other value>
> > Which is also more common in C code compared to
> > return something ? some_value : some_other_value;

Paul Paterson


> Wait, I thought that conditional expressions were supposed to reduce the
> cognitive overload and now here I am having to think up weird and
wonderful
> regular expressions to try to evaluate the cognitive benefit... <wink>

Well, they don't (usually) and you could always modify gcc to generate
that information for you instead of using regexps.

Ahh, sometimes I'm just too funny for words.

Andrew
da...@dalkescientific.com


Max M

unread,
Feb 10, 2003, 6:08:51 AM2/10/03
to
Andrew Dalke wrote:
> Paul Paterson

>
>>If my regular expression is correct (and it probably isn't because I'm not
>>good at them), then a quick scan of my Python22 directory structure finds
>>769 matches of the following pattern,
>>
>> if <something>
>> variable = <some value>
>> else:
>> variable = <some other value>
>
>
> Oh yeah, and you should also check for
>
> if <something>:
> return <some value>
> return <some other value>


Another thing to take into account, is the evolution af a programme.
Often you start up with just one condition:

if <something>
variable = <some value>
else:
variable = <some other value>

You might write this with the terniary op:

<something> ? <some value> : <some other value>

But very often, a few lines later, you will need to at another
condition, or add some temperary calculations.

if <something>:
<new value> = 21*2
variable = <some value>+<new value>
elif: <something else>
variable = <some third value>


else:
variable = <some other value>

Then you will need to rewrite the terniary operator. If you had written
it with an if/else to begin with you wouldn't need to rewrite as much.

In my experience it would hapen so often that using the terniary would
probably require more coding than not using it.

--

hilsen/regards Max M Rasmussen, Denmark

http://www.futureport.dk/
Fremtiden, videnskab, skeptiscisme og transhumanisme

John Roth

unread,
Feb 10, 2003, 7:44:12 AM2/10/03
to

"Andrew Dalke" <ada...@mindspring.com> wrote in message
news:b27mc5$bqc$1...@slb6.atl.mindspring.net...

So? I've seen people do things the hard way in
just about every language I've worked in. In most
cases, all it takes is a friendly suggestion: "Wouldn't
this way be clearer?" and they go "Huh? Oh!" and
the problem goes away.

John Roth
>
> Andrew
> da...@dalkescientific.com
>
>


Jeremy Fincher

unread,
Feb 10, 2003, 10:56:00 AM2/10/03
to
Carlos Ribeiro <crib...@mail.inet.com.br> wrote in message news:<mailman.1044730161...@python.org>...
> While I agree that programmers love to save keystrokes, in some cases is more
> important to save line count. Long blocks of code tend to be hard to read,

But they're significantly easier to read than the same code on a
single line.

> If used **with care**, conditional operators may allow to eliminate a few
> 'auxiliary' if statements out of the way, thereby making the structure
> clearer. Things such as selecting values for printing, etc., don't need to
> stay on the way, and may be distracting when looking at the code.

But *all* the code still needs to be read by anyone wanting to
understand it well. It might as well be on separate lines for ease of
readability.

I may be alone in this, but I find

if condition:
do_something()

To be significantly more readable than:

if condition: do_something()

The indentation shows me the control stucture much better. An
if-expression would make the latter far more common than it already
is.

Jeremy

Andrew Dalke

unread,
Feb 10, 2003, 12:58:05 PM2/10/03
to
John Roth:

> So? I've seen people do things the hard way in
> just about every language I've worked in. In most
> cases, all it takes is a friendly suggestion: "Wouldn't
> this way be clearer?" and they go "Huh? Oh!" and
> the problem goes away.

Answer two questions for me:
1) C/C++ code has the ?: operator. Why then do people use
if (cond) {
x = val1;
} else {
x = val2;
}
when the expression
x = cond ? val1 : val2;
is "better"? Ditto for
if (cond) {
return val1;
}
return val2;

My limited analysis suggests that the first of these options in
C/C++ is used more often than the second one. That suggests
to me that the ternary if/else operator is not generally considered
more useful or at least more memorable, than if/else statements.

2) Suppose you saw some code written
if cond:
x = val1
else:
x = val2

would you suggest that the Python code always be written
x = val1 if cond else val2
? When are the tradeoffs? Eg, we've already seen some
people say that when the lines get too long it is better written
as an if/else statement rather than an if/else expression, so
the answer is not "always"

Or to generalize #2, when should if/else expression be
used and when should it not be used, and how do you
propogate this sense of style and expectations?

Andrew
da...@dalkescientific.com


Laura Creighton

unread,
Feb 10, 2003, 11:03:13 AM2/10/03
to
Jeremy Fincher

> I may be alone in this, but I find
>
> if condition:
> do_something()
>
> To be significantly more readable than:
>
> if condition: do_something()
>
> The indentation shows me the control stucture much better. An
> if-expression would make the latter far more common than it already
> is.
>
> Jeremy

You aren't alone. Far from it.

Laura

Kaz Kylheku

unread,
Feb 10, 2003, 3:31:43 PM2/10/03
to
<crib...@mail.inet.com.br> wrote in message news:<mailman.104467856...@python.org>...
> Why is a ternary operator needed? The main reason is that it allows the
> programmer to clearly express his intentions.

Once upon a time a programming language was invented which
artificially divided computation into statements, which directed the
control flow, and expressions, which evaluated formulas. The creators
looked upon the language, and thanks to their inexperience saw that it
was not bad.

In reality, this division into statements and expressions is nonsense.
You want evaluation control in expressions, which is why the C
language has operators like comma, &&, || and the ternary operator.

The real solution is not to divide the language into expressions and
statements, just have expressions. Then you don't run into stupid
problems, like not being able to use a statement where an expression
is expected. The role of statements is usurped by constructs which
are, syntactically, expressions, but which like the ternary operator
have special evaluation rules.

Kaz Kylheku

unread,
Feb 10, 2003, 3:33:11 PM2/10/03
to
David Eppstein <epps...@ics.uci.edu> wrote in message news:<eppstein-522DB8...@news.service.uci.edu>...
> In article <yu99k7ga...@europa.research.att.com>,
> Andrew Koenig <a...@research.att.com> wrote:
>
> > EXAMPLE 6:
> >
> > p = (s == NULL)? str: s;
> >
> > This example sets a default value to substitute for s in case s is NULL.
> > The likely rewrite:
> >
> > if (s == NULL)
> > p = str;
> > else
> > p = s;
>
> What's wrong with using s || str in this example?

Ah, but the || operator is just another example of control-flow
embedded into an expression, and so illustrates the same need as that
which is satisfied by the ternary operator: the need to have
statement-like evaluation control within an expression.

Alexander Schmolck

unread,
Feb 10, 2003, 4:30:12 PM2/10/03
to
k...@ashi.footprints.net (Kaz Kylheku) writes:

I've expected this argument and I find it difficult to brush aside completely
if each statement type over the years sprouts a strangely differently looking
expression offshoot (lambdas, list comprehensions, "ternary"s).

BTW, have you programmed in python?

alex

Bengt Richter

unread,
Feb 10, 2003, 9:07:25 PM2/10/03
to
On Sat, 08 Feb 2003 18:56:54 +0100, Laura Creighton <l...@strakt.com> wrote:

>> Laura> Because I am one. Because I know so many of them. Because
>> Laura> 'lazy work' and 'hurried work' has been the bane of all
>> Laura> creators since we first started creating anything.
>>
[Andrew Koenig]


>> As an experiment, I went back through a 2800-line C program that I
>> wrote about 20 years ago (!) to see how often I used ?: and in what
>> context. I figure that reading my own 20-year-old code is probably a
>> lot like reading some else's code.
>

>The fact that you would not abuse a ternary does nothing to put my
>mind at rest. I've wished for a world where all the C and C++
>programs I have encountered have been written by you, many times,
>sometimes in those exact words, as well.
>
>If you don't think that it will be used very often, do you admit that it
>might be on the side of 'diminishing returns', a feature request that
>doesn't 'pull enough weight' to be worth doing? Or do you think that any
>language feature that is of use to somebody should automatically go into
>a language to 'add desired functionality'? I doubt very much that you
>believe this, (though if you do, we have found the disagreement, for
>certain). How do you determine if a proposed language feature 'pulls
>enough weight'?
>
My immediate reaction is that philosophy enters into this.
We often hear "you can do it now" when someone asks for a feature,
and this is the case for the ternary. The functionality can be hacked
or rearranged. So it becomes a question of whether it is worthwile to
introduce another choice that might be more concise and robust than
existing choices.

If the introduction were cost-free, it would be a no-brainer. But the
costs and benefits include matters of taste and vary across phases of
development and maintenance and training, etc.

Philosophically, I would rather have a core of simple powerful primitives that
allows me to build a special feature than have a potpourri of everyone's pet
features built in. (The flip side of that is that abuse is easy and tempting).

But how "special" is special? We have seen some evidence that the ternary
is involved in less than 1% of C source code lines. That's pretty special,
so you would want a benefit that was pretty special, unless the feature's
availability was just a natural side effect of doing something "right."

ISTM we also have two stylistic motives that can pull in opposite directions.
I.e., programs as mnemonic narratives of decision/action sequences vs. programs
as algorithms best expressed with something resembling mathematical notation.

IMO both styles are needed, because neither style is always best. ISTM much
of the discussion re the ternary operator reflects preference for one style
or the other. If we have to choose one style, which is most appropriate to
the expression contexts where it will be used?

I confess to preferring mathematical-style notation in expressions.
I just had another idea ;-)
Maybe a variation on Paul's (IIRC) [cond -> expr; cond2 -> expr2; True -> expr3]
using '><' instead of ';' e.g.,

cond -> exprT >< cond2 -> expr2 >< True -> exprF

with a redundant final True condition and its '><' introducer being optional, e.g.
the basic ternary would pare down to:

cond -> exprT -> exprF

this could also have a default exprF if exprT is not followed by >< or ->.
What the default should be is another matter ;-)

Regards,
Bengt Richter

Carlos Ribeiro

unread,
Feb 10, 2003, 8:59:34 PM2/10/03
to
On Monday 10 February 2003 14:03, Laura Creighton wrote:
> Jeremy Fincher

> > I may be alone in this, but I find
> >
> > if condition:
> > do_something()
> >
> > To be significantly more readable than:
> >
> > if condition: do_something()
>
> [snip]

>
> You aren't alone. Far from it.

I agree with it, as far as the test is really a 'control condition'. But
sometimes we do need simple tests - I call them 'auxiliary tests', for small
stuff such as making an option to write out 'item' or 'items', or selecting
alternate colors for a report. Having all those tests unfolded only serves to
make the logic less readable. That's why I like them as conditional
expresssions.

--


Carlos Ribeiro
crib...@mail.inet.com.br

Anders Hammarquist

unread,
Feb 11, 2003, 5:56:02 PM2/11/03
to
In article <b27i4g$u5t$1...@slb9.atl.mindspring.net>,

Andrew Dalke <ada...@mindspring.com> wrote:
>Paul Rubin on my analysis of some C++ code's use of "?:" :
>> > EXAMPLE 1: (used 6 times)
>> >
>> > return arr[i] & mask ? true : false;
>
>> > If C++ does have a boolean type, then this isn't needed. Even
>> > when it didn't exist, it would have been easy to define, just like
>> > the values 'true' and 'false' were defined.
>>
>> It's still needed, if you want the function to return true or
>> false, instead of considering 0x1 and 0x04000 to be equivalent.
>
>If C++ has a bool type and builtin true/false then the C++ code
>quoted above can be replaced with
>
> return bool(arr[i] & mask)

Since we're in the process of tearing all the other languages apart,
I offer this for C/C++ bool when you want a 1 or 0 result (remember,
this is C, and C loves punctuation):

return !!(arr[i] & mask);

While I don't think that's particularly beautiful either, not not
parses easier than a ternary for me at least. I also suspect that
this construct would be a lot more common if C didn't have a
ternary. ;-)

/Anders

--
-- Of course I'm crazy, but that doesn't mean I'm wrong.
Anders Hammarquist | i...@cd.chalmers.se
Physics student, Chalmers University of Technology, | Hem: +46 31 88 48 50
G|teborg, Sweden. RADIO: SM6XMM and N2JGL | Mob: +46 707 27 86 87

Andrew Koenig

unread,
Feb 11, 2003, 6:21:46 PM2/11/03
to
>> If C++ has a bool type and builtin true/false then the C++ code
>> quoted above can be replaced with

>> return bool(arr[i] & mask)

Anders> Since we're in the process of tearing all the other languages apart,
Anders> I offer this for C/C++ bool when you want a 1 or 0 result (remember,
Anders> this is C, and C loves punctuation):

Anders> return !!(arr[i] & mask);

In C++, if this statement appears in the body of a function that
is defined as returning a bool, you can write

return arr[i] & mask;

Anders Hammarquist

unread,
Feb 11, 2003, 9:03:32 PM2/11/03
to
In article <b27mc5$bqc$1...@slb6.atl.mindspring.net>,
Andrew Dalke <ada...@mindspring.com> wrote:

>(A true Python expert is unlikely to misuse if/else
>when a better Python solution exists, but I tried to factor
>in that most of the user base is not an expert.)

One of the nice things about Python when I first discovered
it (back in 1.3 days or thereabout) was that it was extremely
quick to master the language. There wes no obscure syntax that
needed to be grasped. I believe this (together with batteries
included) was responsibe for those initial "Hmm, I can't think
of anything more that I need to code to do this, but this can't
be all, can it?" moments. There was no language to fight.

Of course, I too was looking for a ternary, and found the and/or
hack. However, I believe that being forced to use the and/or
hack made me think more about whether a ternary was warranted or
not. Also, using it forced me to learn the Pythonic meaning of
and and or, and so I do not find constructs such as
"spam = eggs and eggs.count()" at all strange. So, in the end,
I think I now write better and more beautiful code as a result
of being forced to abstract away the ternary. (Though the
occasional and/or hack still appears.)

Python is not (or at least has not been) about enabling the
programmer to write code in whatever way she wants (you want the
other P language for that), but about trying to implant good
practices in the programmer, so that she is more likely to choose
a beautiful construct over an ugly one. The only way that this
can be successful is if there are constraints in what can be
done with the language.

BTW, the by far most common complaint I hear from people to whom
I introduce Python is about the fact that it forces you to use
consistent indentation. Now, they usually get over it once they've
done some serious coding, but this is definitely about forcing
the programmer to use good style.

Paul Rubin

unread,
Feb 11, 2003, 9:27:52 PM2/11/03
to
i...@cd.chalmers.se (Anders Hammarquist) writes:
> Also, using it forced me to learn the Pythonic meaning of
> and and or, and so I do not find constructs such as
> "spam = eggs and eggs.count()" at all strange. So, in the end,
> I think I now write better and more beautiful code as a result
> of being forced to abstract away the ternary.

The other interpretation is that Python's lack of conditional
expressions warped your sense of beauty until you found it natural to
write ugly constructs like that ;-).

Anders Hammarquist

unread,
Feb 12, 2003, 9:08:16 AM2/12/03
to
In article <7xk7g62...@ruckus.brouhaha.com>,

Yes, there is that interpretation too ;-). Though in my defense
I have to say that I tend to avoid the "and" constructs, as
they don't sit quite right. "or" constructs, however, I have
no qualms about. "foo = spam or eggs" is quite clear I think.

0 new messages