Hello,

On Mon, 14 Dec 2020 19:39:27 +1100
Steven D'Aprano <st...@pearwood.info> wrote:

> On Mon, Dec 14, 2020 at 01:09:56AM +0300, Paul Sokolovsky wrote:
> 
> > Do you think there can be difference between the following two
> > expressions:
> > 
> > obj.meth()
> > (obj.meth)()
> > 
> > ?  
> 
> Okay, I'll bite.
> 
> Of course there is a difference: the first statement is ten
> characters long, the second is 12 characters long.

Fair enough.

> Are you asking for a semantic difference (the two statements do 
> something different) or an implementation difference (the two
> statements do the same thing in slightly different ways)?

I'm asking for semantic difference, it's even in the post title. But
the semantic difference in not in "what two statements do", but in
"what two statements mean". Difference in "doing" is entailed by
difference in "meaning". And there may be no difference in "doing",
but still difference in "meaning", as the original "1+2+3" vs
"1+(2+3)" example was intended to show. 

> Implementation differences are fairly boring (to me): 

Right. How implementation points are brought into the discussion is to
show the issue. As mentioned above, the actual progression is the
opposite: first there's semantic meaning, then it's implemented. So,
what's the semantic meaning of "a.b()" that it gets compiled with
LOAD_METHOD bytecode?

> it might happen
> to be that some Python interpreters happen to compile the first
> statement into a slightly different set of byte codes to the second.
> I don't care too much about that, unless there are large performance
> (speed or memory) differences.
> 
> For what it is worth, Python 1.5 generates the exact same byte code
> for both expressions; so does Python 3.9. However the byte code for
> 1.5 and for 3.9 are different.

Right, and the question is what semantic (not implementational!) shift
happened in 3.7 (that's the point when it started to be compiled
differently).

> However, the semantics of the two expressions are more or less
> identical in all versions of Python, regardless of the byte-code used.

That's what I'd like to put under scrutiny.

> (I say "more or less" because there may be subtle differences between 
> versions, relating to the descriptor protocol, or lack there of, old
> and new style classes, attribute lookup, and handling of unbound
> methods.)

Right, and exactly those "subtle differences" is what I'd like to
discuss. I'd like to start however with more of abstract model of
difference meaning, but afterwards, if the common ground is found, it
would be interesting to check specific not-exactly-on-surface Python
features which you list.

[]

> > python3.6 -m dis meth_call.py
> > python3.7 -m dis meth_call.py
> > 
> > Then, to try to explain the difference at the suitable level of
> > abstraction.  
> 
> At a suitable level of abstraction, there is no difference. The
> suitable level of abstraction is at the level of the Python execution
> model, where `(expression)` and `expression` mean the same, where the
> brackets are used for grouping.

The level of abstraction I'm talking about is where you look not just
at "`(expression)` vs `expression`" but at:

expression <op> expression   vs   expression <op> (expression)

Where <op> is an arbitrary operator. As we already concluded, those do
have difference, even considering such a simple operator as "+".

So... what can we say about the difference between a.b() and (a.b)()
then?

> > it might
> > be helpful to add the 3rd line:
> > 
> > t = obj.meth; t()  
> 
> That clearly has different semantics from the first two: it has the 
> side-effect of binding a value to the name t.

But that's yet another good argument to introduce block-level scoping
to Python (in addition to already stated great arguments), because then,

(a.b)()

will be *exactly* equivalent to (inside a function):

if 1:
    const t = a.b
    t()

This neither gets affected by the surrounding environment (all variables
introduced are new, regardless of their names), nor affects it (all
variables are block-local, and not visible outside the block).

> I'm not sure where you think this question is going to lead us. 
> Wherever it is, I wish you would get to the point.

I'm sorry if this looks like a quiz, that's not the intention. But I
really would like to see if other people can spot in this stuff what I
spotted (after pondering about it), and I don't want to bias you in any
way by jumping to my "conclusions". I do believe we'll get there, but
then I don't want to be biased myself either. That's why it's
step-by-step process, and I appreciate the people here are willing to
walk it.

> Are you suggesting that we give a *semantic* difference to:
> 
>     ( expression )
> 
> compared to the unbracketed `expression`?

Hopefully, that was answered above. To end the post with the summary,
I'm suggesting that there's difference between:

expression <op> expression   vs   expression <op> (expression)

Which is hopefully hard to disagree with.

Then I'm asking, how consistent are we with understanding and
appreciating that difference, taking the example of:

a.b()   vs   (a.b)() 


(And it's not a purely language-lawyering question, it has practical
consequences.)


-- 
Best regards,
 Paul                          mailto:pmis...@gmail.com
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NXZCLWYLFZUUQBF2ZIDKGOVV3NIC2HQ4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to