PEP-able? Expressional conditions

K

Kay Schluehr

One of the main reasons Pythons anonymous function lambda is considered
to be "broken" is Pythons disability to put statements into expressions
and support full functionality. Many attempts to improve lambdas syntax
had also been attempts to break the expression/statement distinction in
one or the other way. None of this suggestions have ever been
successfull in solving severe problems caused by Pythons indentation
syntax and I guess they will never succeed. On the other hand I do not
see immediately the necessaty to support the full range of Python
constructs in small anonymous functions that are dedicated to fit into
one line.

Instead of pushing statements into expressions one can try to do it the
other way round and model expressions with the functionality of
statements. The most important use-cases are assignments and
conditions. I want to consider conditions only.

In Python conditional statements have the form:

if COND_1:
BLOCK_1
elif COND_2:
BLOCK_2
....
else:
BLOCK_n

Before turning this kind of statement into an expression we have to
restrict the BLOCKs to expressions:

if COND_1:
EXPR_1
elif COND_2:
EXPR_2
....
else:
EXPR_n

Since the conditional statement is traversed sequentially we can
transform it into a sequence of (COND,EXPR) pairs. Finally we have to
recover the conditional semantics.

I want to propose a new associative binary operator that acts on
(COND,EXPR) pairs like a projection on the second EXPR argument of a
pair in case of COND evaluated True.

This function works much like

def cond(pair1, pair2):
COND1,EXPR1 = pair1
if COND1:
return EXPR1
try:
COND2,EXPR2 = pair2
if COND2:
return EXPR2
except TypeError:
return pair2


Alternative syntax proposals:

(a) (COND1,EXPR1) || (COND2,EXPR2)
(b) (COND1,EXPR1) case (COND2,EXPR2)
(c) (COND1,EXPR1) owise (COND2,EXPR2)
(d) (COND1,EXPR1) ? (COND2,EXPR2)

Regards,
Kay
 
T

Terry Hancock

Instead of pushing statements into expressions one can try to do it the
other way round and model expressions with the functionality of
statements.
Alternative syntax proposals:

(a) (COND1,EXPR1) || (COND2,EXPR2)
(b) (COND1,EXPR1) case (COND2,EXPR2)
(c) (COND1,EXPR1) owise (COND2,EXPR2)
(d) (COND1,EXPR1) ? (COND2,EXPR2)

You appear to be reinventing the C "ternary operator". This is
definitely a dead horse. There was already a PEP, and it was
refused.

If you actually want this, you're going to have to implement it
with a function:

def ternary(condition, true_result, false_result):
if condition:
return true_result
else:
return false_result

Almost as good, and you don't have to talk curmudgeons into providing
it for you.

Cheers,
Terry
 
K

Kay Schluehr

Terry said:
You appear to be reinventing the C "ternary operator". This is
definitely a dead horse. There was already a PEP, and it was
refused.

Well, I'm not inspired by C and the operator is not ternary but binary
and associative. Nevertheless the behaviour of the ternary condition
operator exists as a limit case. The expression becomes more a kind of
a horizontal squeezed switch. Therefore the "case" keyword proposal.

It might become more obvious if one chains the expression using more
terms:

(a') (COND1,EXPR1) || (COND2,EXPR2) || ... || (CONDk,EXPRk)
(b') (COND1,EXPR1) case (COND2,EXPR2) case ... case (CONDk,EXPRk)
If you actually want this, you're going to have to implement it
with a function:

def ternary(condition, true_result, false_result):
if condition:
return true_result
else:
return false_result

No, as I explained it is not a ternary operator and it can't easily be
implemented using a Python function efficiently because Python does not
support lazy evaluation. One usually does not want to evaluate all
conditions as well as all the results ( when passing them into the
function ) but evaluate conditional expressions sequentially and stop
at the first true condition. Well I would indeed like to go even
further and introduce lazy tuples this way but I wanted to notice the
responses to an obvious use case first.

Kay
 
T

Terry Reedy

Kay Schluehr said:
No, as I explained it is not a ternary operator and it can't easily be
implemented using a Python function efficiently because Python does not
support lazy evaluation.

By *carefully* using the flow-control operators 'and' and 'or', you can
often get what you want *now*, no PEP required.
One usually does not want to evaluate all
conditions as well as all the results ( when passing them into the
function ) but evaluate conditional expressions sequentially and stop
at the first true condition.

*If* bool(result_expression_i) == True for all i, (except maybe last
default expression), which is true for some actual use cases, then the
following expression evaluates to the result corresponding to the first
'true' condition (if there is one) or to the default:

c0 and r0 or c1 and r1 or c2 and r2... or default.

I have only seen real examples with one and-pair, like (x < 0) and -x or x
for absolute value.

Terry J. Reedy
 
P

Paul Rubin

Terry Hancock said:
def ternary(condition, true_result, false_result):
if condition:
return true_result
else:
return false_result

Almost as good, and you don't have to talk curmudgeons into providing
it for you.

Not the same at all. It evaluates both the true and false results,
which may have side effects.
 
K

Kay Schluehr

Terry said:
By *carefully* using the flow-control operators 'and' and 'or', you can
often get what you want *now*, no PEP required.


*If* bool(result_expression_i) == True for all i, (except maybe last
default expression), which is true for some actual use cases, then the
following expression evaluates to the result corresponding to the first
'true' condition (if there is one) or to the default:

c0 and r0 or c1 and r1 or c2 and r2... or default.

I have only seen real examples with one and-pair, like (x < 0) and -x or x
for absolute value.

Terry J. Reedy

O.K. you win. One can complete this particular evaluation scheme by
introducing a little wrapper:

def Id(val):
return lambda:val

(c0 and Id(r0) or c1 and Id(r1) or c2 and Id(r2)... or Id(default))()

This works for each sequence r0,r1,... without any restictions.

Kay
 
T

Terry Hancock

Not the same at all. It evaluates both the true and false results,
which may have side effects.

If you are depending on that kind of nit-picking behavior,
you have a serious design flaw, a bug waiting to bite you,
and code which shouldn't have been embedded in an expression
in the first place.
 
S

Steven Bethard

Kay said:
O.K. you win. One can complete this particular evaluation scheme by
introducing a little wrapper:

def Id(val):
return lambda:val

(c0 and Id(r0) or c1 and Id(r1) or c2 and Id(r2)... or Id(default))()

This works for each sequence r0,r1,... without any restictions.

Or use a list as the "wrapper":

(c0 and [r0] or c1 and [r1] or c2 and [r2] ... or [default])[0]

Not that I'd actually encourage anyone to write this code. ;-)

STeVe
 
B

Bengt Richter

If you are depending on that kind of nit-picking behavior,
you have a serious design flaw, a bug waiting to bite you,
and code which shouldn't have been embedded in an expression
in the first place.
Or you might know excatly what you are doing ;-)

Regards,
Bengt Richter
 
P

Paul Rubin

Terry Hancock said:
If you are depending on that kind of nit-picking behavior,
you have a serious design flaw, a bug waiting to bite you,
and code which shouldn't have been embedded in an expression
in the first place.

Are you kidding? Example (imagine a C-like ?: operator in Python):

x = (i < len(a)) ? a : None # make sure i is in range

Where's the design flaw? Where's the bug waiting to bite? That's a
completely normal use of a conditional expression. If the conditional
expression works correctly, this does the right thing, as intended.
If both results get evaluated, it throws a subscript out of range
error, not good.

Every time this topic comes up, the suggestions that get put forth are
far more confusing than just adding conditional expressions and being
done with it.
 
A

Antoon Pardon

Op 2005-09-07 said:
By *carefully* using the flow-control operators 'and' and 'or', you can
often get what you want *now*, no PEP required.

Which is why I don't understand the resistance against introducing
such a beast.

Whether it is a ternary operator or something more general, the
proposed construction are usually more readable than the
python construction with the same functionality.


The decorator syntax IMO provided much less improvement in
readability for functionality that was already provided
and that got implemented.


A ternary operator (or suitable generalisation) would IMO
provide a greater improvement what readability is concerned
but is resisted all the way.
 
A

Antoon Pardon

Op 2005-09-08 said:
The idea has already been discussed to death. Read PEP 308 to see what was
proposed, discussed, and why the PEP was eventually rejected:

So what? If the same procedure would have been followed concerning
the decorator syntax, it would have been rejected too for the same
reasons.

IMO this is worded in a misleading way. It wasn't that there was lack
of an overwhelming majority for change. 436 supported at least one
of the options and only 82 rejected all options. So it seems 436 voters
out of 518 supported the introduction of a ternary operator.

Yes no format was able to draw majority support but that is hardly
suprising since there where 17 formats to choose from. I find
the fact that people were unable to choose one clear winner out
of 17 formats in one vote, being worded as: "lack of an overwhelming
majority for change" at the least not very accurate and probably
misleading or dishonest.
 
T

Terry Hancock

Terry Hancock said:
If you are depending on that kind of nit-picking behavior,
you have a serious design flaw, a bug waiting to bite you,
and code which shouldn't have been embedded in an expression
in the first place.

Are you kidding? Example (imagine a C-like ?: operator in Python):

x = (i < len(a)) ? a : None # make sure i is in range

Where's the design flaw?


It's a syntax error. See?
x = (i < len(a)) ? a : None # make sure i is in range

File "<stdin>", line 1
x = (i < len(a)) ? a : None # make sure i is in range
^
SyntaxError: invalid syntax

That's a pretty serious design flaw.

You see, I'm not discussing an imaginary Python interpreter here.
Where's the bug waiting to bite? That's a
completely normal use of a conditional expression. If the conditional
expression works correctly, this does the right thing, as intended.
If both results get evaluated, it throws a subscript out of range
error, not good.

No. It throws a syntax error, as I demonstrated.
Every time this topic comes up, the suggestions that get put forth are
far more confusing than just adding conditional expressions and being
done with it.

If it's so easy, then do it, and quit whining. You could easily
have published an alternative "better" ternary function instead
of just complaining that mine "isn't right", by which you mean
only that it doesn't work exactly as it would in C, or in your
imaginary Python interpreter.

Myself, I just can't see how writing:

if (i < len(a)): x = a
else: x = None

is gonna kill you. It's also not by any means a solution so obvious
that I would not want to see the special case on it's own line.

After all, in most cases where I would use a limiter like this, I'd
want the special case to be handled by something other than None. Or
I'd want to raise an exception, even. Which, come to think of it,
I wouldn't even have to code, would I? The thing is, if you return
None like this, you won't be able to treat it the same as the normal
output, so you're still going to have to check the result. Which just
means you've moved the complexity upwards (almost always a bad move
which replicates effort -- another design flaw).

If you want to go and argue with Guido over this, go ahead. I'm all
for a ternary operator.

But IIRC, that parrot is dead. It's an ex-parrot.

Hence, I'm not big on discussing what sort of fake wings we can
glue onto it. It isn't that hard to live without a ternary operator.

You're just trying to program a C idiom in Python and cussing the
fact that it doesn't translate. Sorry, loads of stuff doesn't, but
it doesn't necessarily make that a design flaw.

But if you really must avoid evaluating the results in your hand-crafted
ternary function, that is of course, also possible. Go ahead and
write it and share it here, please.
 
A

Antoon Pardon

Op 2005-09-09 said:
Terry Hancock said:
Not the same at all. It evaluates both the true and false results,
which may have side effects.

If you are depending on that kind of nit-picking behavior,
you have a serious design flaw, a bug waiting to bite you,
and code which shouldn't have been embedded in an expression
in the first place.

Are you kidding? Example (imagine a C-like ?: operator in Python):

x = (i < len(a)) ? a : None # make sure i is in range

Where's the design flaw?


It's a syntax error. See?
x = (i < len(a)) ? a : None # make sure i is in range

File "<stdin>", line 1
x = (i < len(a)) ? a : None # make sure i is in range
^
SyntaxError: invalid syntax

That's a pretty serious design flaw.

You see, I'm not discussing an imaginary Python interpreter here.


Nobody is, but the general consensus about a ternary operator
has always been that it should have lazy evaluataion.
That is even mentioned in the PEP.
http://www.python.org/peps/pep-0308.html

The BDFL's position is that short-circuit behavior is essential
for an if-then-else construct to be added to the language.

And you did claim that depending on side effects was a serious design
flaw. And as far as I understand what you wrote that was independant
of the language used.

No. It throws a syntax error, as I demonstrated.

Which is a dishonest remark. The way you made the remark, made it
a claim about algorithmic design, independant of the language
chosen or the specific construct chosen to interpret it.

That Paul chose a construct that is not syntactic python to dispute
your claim, is beside the point.
If it's so easy, then do it, and quit whining. You could easily
have published an alternative "better" ternary function instead
of just complaining that mine "isn't right", by which you mean
only that it doesn't work exactly as it would in C, or in your
imaginary Python interpreter.

There have been alternatives enough that have been published.
It is not a matter of publishing yet an other alternative.
It is just that the BDFL has declare this a no no.
Myself, I just can't see how writing:

if (i < len(a)): x = a
else: x = None

is gonna kill you. It's also not by any means a solution so obvious
that I would not want to see the special case on it's own line.


And how do I do this in a list comprehension?

Myself I couldn't see how writing:

def fun(...):
...

fun = deco(fun)

Would kill anyone, but decorator syntax appeared anyway.
After all, in most cases where I would use a limiter like this, I'd
want the special case to be handled by something other than None. Or
I'd want to raise an exception, even. Which, come to think of it,
I wouldn't even have to code, would I? The thing is, if you return
None like this, you won't be able to treat it the same as the normal
output, so you're still going to have to check the result. Which just
means you've moved the complexity upwards (almost always a bad move
which replicates effort -- another design flaw).

Examples given here, are done so to make a point. The example itself
may never been used in actual code, but code that is similar enough
in behaviour, may. Try to look at the argument one is trying to make
instead of looking at insignificant details.
If you want to go and argue with Guido over this, go ahead. I'm all
for a ternary operator.

But IIRC, that parrot is dead. It's an ex-parrot.

Yes probably, but the fact that it is a dead parrot, doesn't make
the idea bad. I sometimes get the impression that just because
Guido declared a subject dead, some people feel obligated to
argue how bad it would have been anyway.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top