[Python-ideas] Syntax: 'return: ...' expressions

Ron Adam ron3200 at gmail.com
Sat Jan 24 08:53:34 CET 2015



On 01/24/2015 12:06 AM, Andrew Barnert wrote:
> On Friday, January 23, 2015 2:06 PM, Ron Adam<ron3200 at gmail.com>
> wrote:
>
>>>>>
>>>
>>> On 01/23/2015 08:56 AM, Nick Coghlan wrote:

>>>>> I really like that "each statement is like a single step in a
>>>>> mathematical proof" analogy.
>>>
>>> I agree.
>>>
>>>
>>> There are some other distinctions.  When you consider these, you can
>>> see how many language designers come to similar solutions.
>>>
>>>
>>> Expressions evaluate in unique name spaces, while statements
>>> generally do not. Consider "a + b"; it is evaluated in a private
>>> method after the values a and b are passed to it.

> I don't think that's true.

Not all the time, which is why I said "generally". :-)

> Consider "a[b] = c", which is a statement,
> but it's evaluated in a private method after the values a, b, and c are
> passed to it. The fact that it's a.__setitem__ rather than a.__add__
> doesn't seem particularly important. I think the key is that __setitem__
> doesn't have a return value--like (nearly) everything in Python that
> mutates state--and therefore there's no expression for it. The question
> is, what does that buy?

Right, and good question, but consider that it's not adding or altering the 
name space the 'a' object is in.  It's a convenience/exception for using 
objects.

If objects were done with closures, then the same modification would be 
done with an assignment statement.  And then the generalisation would be 
more consistent.  But at a cost is other ways.

>>> Statements are used to mutate the current name space, while
>>> expressions generally do not.
> This is closer. I think, more generally, statements are used to mutate
> the important state that the local function is all about mutating. And
> the thing that gets mutated is almost always the leftmost thing. That
> definitely helps in scannability.

I'm not quite sure I follow that.  I think what you are calling the 
important state, I think of as the shared state. A value that will be used 
multiple times in the same frame.

Binding a name to a value mutates the names pace, but it does not mutate 
the name.  It's still the same name.

Again this can be view multiple way if you consider how it actually works. 
  Some languages use a stack to implement a name. And in a new frame, a new 
bound value would get pushed on the name stack.  I'm not completely sure 
that python doesn't do something like that in some places to speed thing 
up.  But I don't think so.

>>> Statements can alter control flow, while expressions generally do
>>> not.
> Sure, but I think this part only really helps if control flow is somehow
> visible. In Python, it is, because control flow almost always means
> compound statements, which means indentation, and very little else means
> indentation.

You are referring to the visual aspect, while I'm referring to what a 
statement does.  Same thing. ;-)

>>> Having a clear distinction between expressions and statements makes
>>> reading and understanding code much easier.
> Definitely. That's the part I think is key, but am struggling to
> explain.
>
> I think Guido offers a great analogy in mathematical proofs, but the
> question is to find the actual commonality behind the analogy.

Form follows function...  Just one way to look at it.  Sometimes what 
something does can come from the shape it has too.


> After some more thought on this, I think what it comes down to is that
> (idiomatically-written) Python lets you skim the control flow (because
> all non-trivial control flow, and very little else, is expressed in
> terms of indentation) and the state transitions (because each statement
> generally mutates at most one thing, and it's the leftmost thing), so
> you can quickly find the part of the code you actually need to read
> carefully, instead of having to read the whole thing.
>
> I've written this idea up in a bit more detail here:
>
> http://stupidpythonideas.blogspot.com/2015/01/statements-and-expressions.html

Very interesting.. and thanks for the mention.  ;-)

One of the things that makes a difference is to be able to hold a 
simplified model in your mind while you are working on it.  The separation 
of statements and expressions definitely helps me with that.  If I can 
easily keep the name space in my mind... or at least the part of it that 
correspond with the block of code I'm looking at, It really helps me to 
visualise things in a meaningful way and see what effect the statements 
will have on the name space.

When statements and expressions don't represent what they do in an obvious 
way, then all bets are off.  It becomes a mental stumbling block.

Much of this became clear to me when I wrote a simplified script language 
that adds statements to a mini scheme like language.  I did need to use 
braces for blocks.  Even though I didn't need to use visual indentation, I 
still preferred formatting the programs in a similar style to python.  In 
this mini language, the separation of statements and expressions is even 
more pronounced because all expressions are s-expressions, and all 
statements are not expressions.

And going one bit further, I used dictionaries for names spaces, like 
python, but used lists for statement blocks.  So statement blocks have 
order, but name spaces don't.


>>> I believe Python follows most of these conventions in most places,
>>> and when it doesn't, it's usually for a practical reason that are
>>> fairly obvious.
>>>
>>> For example, an "or" expression is a bit of both.
>>>
>>> Another example of how python chooses a practical alternative is we
>>> can nest expressions instead of using "call" statements on separate
>>> lines and a stack to hold the augments and return values.  That is
>>> what python does in the byte code so we don't have to do it in
>>> explicit statements.
>>>
>>> If you factor out all expressions you get byte code.  Or if you
>>> factor out all statements you get something like lisp.
> I don't think either part of that is true.

It wasn't meant to be taken absolutely literally.  Which is why I said... 
"something like".  There is quite a bit of wiggle room when it comes to how 
different people think about things, and what words we use to describe 
them.  Some times the hardest part of a discussion is getting the same 
mental picture in more than one person.  :-)

> Bytecode is full of things
> that are expressions—even your paradigm case of an operator expression
> is handled by an opcode.

I see opcodes as being similar to keywords that are used in a statement 
form.  And I view the stack as being a temporary value space.  Of course, I 
know certain combinations of several opcodes together may correspond to a 
particular python expression, but individually, each byecode along with 
it's following few values, are byte code statements to me.

And even if a single bytecode was the equivalent of a python expression, my 
point was you can use statements replace statements with expressions.

Byte code is a good example of that.  But you still have functions calls.. 
You just can't nest them in the same way you do with python function calls, 
you must push them on the stack and use CALL "statements" to execute it, 
and then use another statement to store the value that gets put on the top 
of the stack.  (or push another function on the stack that will use that 
value...)


> And conversely, CoffeeScript (if you avoid
> break/continue/return statements) factors out all statements, and Ruby
> comes close to doing so, and yet they're really not more Lisp-like than
> Python in any meaningful way.

See the next paragraph... ;-)

>>> Of course this subject is definitely a very subjective one which
>>> relays on agreeing on the general meaning of the above sentences.
>>> OR... YMMV.



More information about the Python-ideas mailing list