On 13May2018 08:55, Chris Angelico <ros...@gmail.com> wrote:
On Sun, May 13, 2018 at 8:47 AM, Juancarlo Añez <apal...@gmail.com> wrote:

My main point here is that "with" works as well as "given" in this form
from an English prose point of view.


+1 for "with...as", -1 for ":="

About affecting existing contexts, it seems that "with..as" would create a
new context just for the expression, and the control statement it is
embedded in, similar to what the current "with" statement does. These are
semantics that are really easy to explain.

The trouble with every variant involving 'with' is that the semantics
LOOK similar, but are subtly different. The current 'with' statement
doesn't create a subscope; the only "context" it creates is regarding
the resource represented by the context manager. For instance, opening
a file in a 'with' block will close the file at the end of the block -
but you still have a (closed) file object. Using "with... as" for name
bindings wouldn't call __enter__ or __exit__, so it won't create that
kind of context; and whether it creates a subscope for the variable or
not, it's not going to match the 'with' statement.

For myself, I'm not a fan of a narrow scope. I'd be happy with an inline assignment landing in the function scope (or whatever the current innermost scope is). Like normal assignments. In how many other places does Python adopt a narrower scope than the function/method/class/module?

Consider:

 r = re.compile("bah(baz)")
 if m given m = r.match("foo barbaz"):
   thing = m.group(1)

I _want_ to access "m" after the expression.

My other dislike of narrow scopes is shadowing. I had an unpleasant experience last year working in Go, which has its own ":=" assignment. While Go's := semantics are a little different to the assignment expressions being considered here, it has an IMO dangerous shadowing effect.

I'm going to digress into an explaination of this issue in Go here because I want to highlight my dislike of easy accidental shadowing, which is a problem in many areas, and I think that conflating inline assignment with an implied narrow scope in Python would make this worse.

In Go you can declare variables 2 ways:

 var ok = True

and:

 ok := True

Also, because Go doesn't use exceptions a common idiom is to return a success/fail value and the task's result:

 ok, result := do_something(...)

That handily declares "ok" and "result" and gives them values.

Because the ":=" is so convenient, you might often declare variables as they get used:


 ok, result1 := do_something()
 ok, result2 := do_something_else()

and so on. You're allowed to mix existing names with new (undeclared) names provided there's at least one new name left of the ":=".

So far this is all just ordinary assignments, with convenient declaration included. But consider the function that calls other functions:

 func thing_outer(x) {
     ok, result1 := do_something()
     if ok {
       ok, result2 := do_something_else()
       if ok {
         fmt.Println("ok!")
       }
     }
     return ok, result
 }

That's how it might go in Pythonic form. But Go lets you preceed the test condition with a statement, somewhat like the C "for (setup values; test; advance)" form:

 for (i=0; i<10; i++)

So you may wish to write the function like this:

 func thing_outer(x) {
     if ok, result1 := do_something(); ok {
       if ok, result2 := do_something_else(); ok {
         fmt.Println("ok!")
       }
     }
     return ok, result
 }

and here is where the scoping causes a disaster.

In Go, the declarations _within_ the "if" statement have the "if" statement as their scope. In particular, the inner "ok" shadows the outer "ok", such that an inner failure (setting "ok" to false) doesn't affect the outer "ok" which was true. And so the overall function returns apparent success.

And that is entirely enabled by the implied scoping in the "if" statement.

The above example is subtly wrong because I'm doing this from memory, but I had a very simple real world example bite me this way and it was a PITA to debug because the effect was so surprising. So much so that from then on I pretty much eschewed the ":=" declaration as a dangerous construct and went with "var" all the time, effectively giving me _one_ scope within any function.

Cheers,
Cameron Simpson <c...@cskk.id.au>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to