Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: as equivalent to: def __use_stmt(): use-suite def _in_clause(): in-suite return names return _in_clause() __use_stmt_args = {} names = __use_stmt() del __use_stmt The more I think about this return-based approach, the less I like it. It could probably be made to work, but it just feels like a kludge to work around the fact that the only mechanisms available for altering the bindings of local names are assignment and definition statements. For class namespaces, getattr(), setattr() and delattr() work a treat, and globals() works fine for module level name binding. locals() is an unfortunate second class citizen, since it writes to it aren't propagated back to the executing frame. Programmatic interrogation of locals is fine, but update is impossible. What would be interesting is if locals() returned a dictionary whose __setitem__ method invoked PyFrame_LocalsToFast on the relevant frame, instead of a vanilla dictionary as it does now. Then locals()[x] = foo would actually work properly. Notice that you can get this effect today, by using exec to force invocation of PyFrame_LocalsToFast: Py def f(): ... n = 1 ... def g(outer=locals()): ...outer[n] += 1 ... g() # Does not affect n ... print n ... exec g() # DOES affect n ... print n ... Py f() 1 2 (The call to g() has to be inside the exec statement, since the exec statement evaluation starts with a call to PyFrame_FastToLocals). Assuming a writeable locals(), the semantics for the normal case are given by: def __use_stmt(__outer): use-suite in-suite __inner = locals() for name in names: __outer[name] = __inner[name] __use_stmt(locals()) del __use_stmt And for the 'delayed execution' case: def __named_use_stmt(__outer): use-suite def __delayed_block(): in-suite __inner = locals() for name in names: __outer[name] = __inner[name] return __delayed_block in-name = __named_use_stmt(locals()) del __named_use_stmt Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() I've come to the conclusion that these semantics aren't what I would expect from the construct. Exactly what I would expect can't really be expressed in current Python due to the way local name bindings work. The main thing to consider is what one would expect the following to print: def f(): a = 1 b = 2 print 1, locals() print 3, locals() using: a = 2 c = 3 print 2, locals() print 4, locals() I think the least suprising result would be: 1 {'a': 1, 'b': 2} # Outer scope 2 {'a': 2, 'c': 3} # Inner scope 3 {'a': 2, 'b': 2, 'c': 3} # Bridging scope 4 {'a': 1, 'b': 2} # Outer scope In that arrangement, the statement with a using clause is executed normally in the outer scope, but with the ability to see additional names in its local namespace. If this can be arranged, then name binding in the statement with the using clause will work as we want it to. Anyway, I think further investigation of the idea is dependent on a closer look at the feasibility of actually implementing it. Given that it isn't as compatible with the existing nested scope structure as I first thought, I suspect it will be both tricky to implement, and hard to sell to the BDFL afterwards :( Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: def f(): a = 1 b = 2 print 1, locals() print 3, locals() using: a = 2 c = 3 print 2, locals() print 4, locals() I think the least suprising result would be: 1 {'a': 1, 'b': 2} # Outer scope 2 {'a': 2, 'c': 3} # Inner scope 3 {'a': 2, 'b': 2, 'c': 3} # Bridging scope 4 {'a': 1, 'b': 2} # Outer scope Personally, I think that the fact that the bridging statement is executed *after* the inner code block guarantees that results will be surprising. The fact that it effectively introduces *two* new scopes just makes matters worse. It also seems to me that one could do this using a nested function def with about the same results. You wouldn't have a bridging scope with both sets of names as locals, but your nested function would have access to the outer namespace via normal nested scopes, so I'm really not seeing what the gain is... (Then again, I haven't been following the whole using/where thread, because I don't have that much free time and the initial postings failed to convince me that there was any real point...) Jeff Shannon Technician/Programmer Credit International In that arrangement, the statement with a using clause is executed normally in the outer scope, but with the ability to see additional names in its local namespace. If this can be arranged, then name binding in the statement with the using clause will work as we want it to. Anyway, I think further investigation of the idea is dependent on a closer look at the feasibility of actually implementing it. Given that it isn't as compatible with the existing nested scope structure as I first thought, I suspect it will be both tricky to implement, and hard to sell to the BDFL afterwards :( Cheers, Nick. -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
On Fri, 14 Jan 2005 01:48:48 +1000, Nick Coghlan [EMAIL PROTECTED] wrote: Nick Coghlan wrote: Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() I've come to the conclusion that these semantics aren't what I would expect from the construct. Exactly what I would expect can't really be expressed in current Python due to the way local name bindings work. The main thing to consider is what one would expect the following to print: def f(): a = 1 b = 2 print 1, locals() print 3, locals() using: a = 2 c = 3 print 2, locals() print 4, locals() I think the least suprising result would be: 1 {'a': 1, 'b': 2} # Outer scope 2 {'a': 2, 'c': 3} # Inner scope 3 {'a': 2, 'b': 2, 'c': 3} # Bridging scope 4 {'a': 1, 'b': 2} # Outer scope In that arrangement, the statement with a using clause is executed normally in the outer scope, but with the ability to see additional names in its local namespace. If this can be arranged, then name binding in the statement with the using clause will work as we want it to. Anyway, I think further investigation of the idea is dependent on a closer look at the feasibility of actually implementing it. Given that it isn't as compatible with the existing nested scope structure as I first thought, I suspect it will be both tricky to implement, and hard to sell to the BDFL afterwards :( In the timbot's let/in format: def f(): a = 1 b = 2 print 1, locals() let: a = 2 c = 3 print 2, locals() in: print 3, locals() print 4, locals() I think the effect would be as if def f(): ... a = 1 ... b = 2 ... print 1, locals() ... def __unique_temp(): ... a = 2 ... c = 3 ... print 2, locals() ... def __unique_too(): ... print 3, locals() ... __unique_too() ... __unique_temp() ... del __unique_temp ... print 4, locals() ... f() 1 {'a': 1, 'b': 2} 2 {'a': 2, 'c': 3} 3 {} 4 {'a': 1, 'b': 2} print 3, locals() doesn't show a,b,c in locals() unless you use them somehow in that scope, e.g., def f(): ... a = 1 ... b = 2 ... print 1, locals() ... def __unique_temp(): ... a = 2 ... c = 3 ... print 2, locals() ... def __unique_too(): ... print 3, locals(), (a,b,c) # force references for locals() ... __unique_too() ... __unique_temp() ... del __unique_temp ... print 4, locals() ... f() 1 {'a': 1, 'b': 2} 2 {'a': 2, 'c': 3, 'b': 2} 3 {'a': 2, 'c': 3, 'b': 2} (2, 2, 3) 4 {'a': 1, 'b': 2} Of course, locals() does not include globals, even though they're referenced and visible: b 'global b' def bar(): ... print locals(), b ... bar() {} global b The trouble with this is that bindings created in __unique_too all get thrown away, and you wouldn't want that limitation. So I proposed specifying the (re)bindable names in a parenthesized list with the let, like let(k, q, w): ... so that those names would be (re)bindable in the same scope as the let(...): statement. As an extension, I also proposed optionally binding __unique_temp to a specified name and not calling it automatically, instead of the automatic call and del. That provides new ways to factor updates to local namespaces into local functions with selective write-through (bind/rebind) to local names. E.g., # define case blocks for switch # better sugar later, this is to demo functinality ;-) #a let(x): k = 123 in foo: x = k #b let(x, y): q = 456 from math import pi as r in bar: x = q y=r # extra binding created if bar is called #c let(x):in baz:x=789 # most compact form, where nothing in the let clause switch = dict(a=foo, b=bar, c=baz) Now you can update local bindings with a case switch: x = 0 case = 'b' print x # = 0 switch[case]() # executes bar() in this example, which assigns local x=456 and y=pi print x # = 456 This spare example is easy to dismiss, but think of foo, bar, and baz as arbitrary sequences of statements in the local namespace, except you can factor them out as a single named group and invoke them safely by name(), and have them affect only the local names you specify in the group's let(x, y, ...): spec. It provides a new way of factoring. As well as things no one has thought of yet ;-) The other thing to think about is that the let suite could be strictly def-time, which would provide the opportunity to avoid re-calculating things in functions without abusing default args, and using the easy closure-variable creation instead. E.g., let(foo): preset = big_calc() in: def foo(x): return x * preset (This in: has no in xxx: name, so the effect is immediate execution of the anonymously defined function, which
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Bengt Richter wrote: Problems? (Besides NIH, which I struggle with regularly, and had to overcome to accept Tim's starting point in this ;-) The ideas regarding creating blocks whose name bindings affect a different scope are certainly interesting (and relevant to the 'using' out-of-order execution syntax as well). Out-of-order execution appeals to me, but the ability to flag 'hey, this is just setup for something I'm doing later' might be a reasonable alternative (particularly with the affected names highlighted on the first line). As Jeff pointed out, it would be significantly less surprising for those encountering the construct for the first time. Folding code editors would be able to keep the setup clause out of the way if you really wanted to hide it. On the other hand, it might be feasible to construct a virtually identical out-of-order two suite syntax, similar to the mathematical phrasing let f = c/lambda where f is the frequency, c is the speed of light and lambda is the wavelength. Either way, you've convinced me that two suites (and a new compound statement), as well as specifying which names can be rebound in the containing scope, is a better way to go than trying to mess with the definition of Python statements. On keywords, while 'let' is nice for assignments, I find it just doesn't parse properly when I put function or class definitions in the clause. So, I'll swap it for 'use' in the examples below. The statement could then be read use these outer bindable names, and this additional code, in this suite. YMMV, naturally. Let's consider some of the examples given for 'where' using an in-order let/in type syntax (the examples only bind one name at a time, but would allow multiple names): # Anonymous functions use res: def f(x): d = {} exec x in d return d in: res = [f(i) for i in executable] # Declaring properties class C(object): use x: def get(self): print Demo default def set(self, value): print Demo default set in: x = property(get, set) # Design by contract use foo: def pre(): pass def post(): pass in: @dbc(pre, post) def foo(): pass # Singleton classes use C: class _C: pass in: C = _C() # Complex default values use f: def default(): return Demo default in: def f(x=default()): pass They actually read better than I expected. Nicely, the semantics of this form of the syntax *can* be articulated cleanly with current Python: use names: use-suite in: in-suite as equivalent to: def __use_stmt(): use-suite def _in_clause(): in-suite return names return _in_clause() __use_stmt_args = {} names = __use_stmt() del __use_stmt Those semantics don't allow your switch statement example, though, since it doesn't use any magic to write to the outer scope - it's just a normal return and assign. However, I don't think starting with these semantics would *preclude* adding the ability to name the second block at a later date, and make the name rebinding part of executing that block - the standard usage doesn't really care *how* the names in the outer scope get bound, just so long as they do. Whether I think that's a good idea or not is an entirely different question :) Another aspect to consider is whether augmented assignment operations in the inner-scopes should work normally - if so, it would be possible to alter the semantics to include passing the existing values as arguments to the inner scopes. Moving on to considering a two-suite out-of-order syntax, this would have identical semantics to the above, but a syntax that might look something like: as names: in-suite using: use-suite # Anonymous functions as res: res = [f(i) for i in executable] using: def f(x): d = {} exec x in d return d # Declaring properties class C(object): as x: x = property(get, set) using: def get(self): print Demo default def set(self, value): print Demo default set # Design by contract as foo: @dbc(pre, post) def foo(): pass using: def pre(): pass def post(): pass # Singleton classes as C: C = _C() using: class _C: pass # Complex default values as f: def f(x=default()): pass using: def default(): return Demo default Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() Bleh. Not only was my proposed grammar change wrong, my suggested semantics are wrong, too. Raise your hand if you can see the problem with applying the above semantics to the property descriptor example. So I think the semantics will need to be more along the lines of pollute the namespace but mangle the names so they're unique, and the programmer can *act* like the names are statement local. This will be much nicer in terms of run-time performance, but getting the locals() builtin to behave sensibly may be a challenge. Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() Bleh. Not only was my proposed grammar change wrong, my suggested semantics are wrong, too. Raise your hand if you can see the problem with applying the above semantics to the property descriptor example. So I think the semantics will need to be more along the lines of pollute the namespace but mangle the names so they're unique, and the programmer can *act* like the names are statement local. This will be much nicer in terms of run-time performance, but getting the locals() builtin to behave sensibly may be a challenge. afair you told yourself that var = statement where: suite translates to: def unique_name(): suite return statement var = unique_name() in this case class gets unique_name() function? is it that bad? anyway I'd prefer to change semantics deeper. adding new statement-only scope and adding our suite-definitions there. -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: Nick Coghlan wrote: Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() Bleh. Not only was my proposed grammar change wrong, my suggested semantics are wrong, too. Raise your hand if you can see the problem with applying the above semantics to the property descriptor example. Eh, never mind. The following works today, so the semantics I proposed are actually fine. (This is exactly the semantics proposed for the property example) Py class C(object): ... def _x(): ... def get(self): ... print Hi! ... def set(self, value): ... print Hi again! ... def delete(self): ... print Bye ... return property(get, set, delete) ... x = _x() ... Py C.x property object at 0x009E6738 Py C().x Hi! Py C().x = 1 Hi again! Py del C().x Bye Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Andrey Tatarinov wrote: afair you told yourself that var = statement where: suite translates to: def unique_name(): suite return statement var = unique_name() in this case class gets unique_name() function? is it that bad? No, I wasn't thinking clearly and saw problems that weren't there. However, you're right that the semantic definition should include unbinding the unique name after the statement finishes. E.g. for assignments: def unique_name(): suite return expr target = unique_name() del unique_name anyway I'd prefer to change semantics deeper. adding new statement-only scope and adding our suite-definitions there. A new scope essentially *is* a nested function :) My main purpose with the nested function equivalent is just to make the intended semantics clear - whether an implementation actually _does_ things that way is immaterial. Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Andrey Tatarinov wrote: I think using 'with' keyword can cause some ambiguity. for example I would surely try to write x = a+b with self: b = member and using with at the end of block brings more ambiguity: stmt1() stmt2() with self: member = stmt3() compare to: stmt1() stmt2() with: variable = stmt3() a way different semantics with just one word added/deleted. Except that for a with expr: block, attributes of the expression must be preceded by a dot: Py stmt1() Py stmt2() Py with self: ....member = stmt3() The advantages of the 'with' block are that 'self' is only looked up once, and you only need to type it once. The leading dot is still required to disambiguate attribute references from standard name references. Despite that, I think you are right that the ambiguity is greater than I first thought. Correct code is reasonably easy to distinguish, but in the presence of errors it is likely to be unclear what was intended, which would make life more difficult than it needs to be. However, I still agree with Alex that the dual life of where outside of Python (as an 'additional definitions' clause, as in mathematics, and as a 'conditional' clause, as in SQL), and the varied background of budding Pythoneers is a cause for concern. 'in' is worth considering, as it is already used by Python at least once for declaring use of a namespace (in the 'exec' statement). However, I suspect it would suffer from ambiguity problems similar to those of 'with' (consider expr in expr and expr in: expr). There's also the fact that the statement isn't *really* executed in the inner namespace - any name binding effects are seen in the outer scope, whereas 'exec x in dict' explicitly protects the containing namespace from alteration. So of the four keywords suggested so far ('where', 'with', 'in', 'using'), I'd currently vote for 'using' with 'where' a fairly close second. My vote goes to 'using' because it has a fairly clear meaning ('execute the statement using this extra information'), and doesn't have the conflicting external baggage that 'where' does. Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
So of the four keywords suggested so far ('where', 'with', 'in', 'using'), I'd currently vote for 'using' with 'where' a fairly close second. My vote goes to 'using' because it has a fairly clear meaning ('execute the statement using this extra information'), and doesn't have the conflicting external baggage that 'where' does. I should agree with you =) Though I love with for historical reasons and addiction to functional languages using is not that bad and I do not mind using it. =) -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Duncan Booth wrote: Nick Coghlan wrote: Grammar Change -- Current:: statement ::=stmt_list NEWLINE | compound_stmt New:: statement ::=(stmt_list NEWLINE | compound_stmt) [local_namespace] local_namespace ::= with : suite Semantics - The code:: statement with: suite translates to:: def unique_name(): suite statement unique_name() Your proposed grammar change says that you need a newline after the statement: True, and not really what I intended. However, it does highlight the fact that statement lists haven't been considered in the discussion so far. If statement lists are permitted, getting the right targets bound in the containing scope is going to be fun for things like this: a = b = 2; c = 3; print a + b with: pass (Probably not impossible though - e.g. it may be feasible to figure out all the bindings that have to happen and return an appropriate tuple from the inner scope for binding in the outer scope) It might also be somewhat confusing as to exactly which statements are covered by the local scoping. (All of them would be, but you could be forgiven for assuming it was just the last one) I think the following would work as a version of the grammar which permits local namespaces for statement lists: statement ::= (stmt_list (NEWLINE | local_namespace)) | (compound_stmt [local_namespace]) local_namespace ::= with : suite Disallowing local namespaces for statement lists would suggest something like this: statement ::= (simple_stmt (NEWLINE | ; stmt_list NEWLINE | local_namespace) ) | (compound_stmt [local_namespace]) local_namespace ::= with : suite I'm pretty sure the permissive version is legal for the CPython parser, and I think the somewhat baroque structure I've used for the restrictive version makes it legal, too. Disallowing local namespaces for statement lists might be a good place to start, since it gives an easier target for a first implementation. Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
Nick Coghlan wrote: Disallowing local namespaces for statement lists would suggest something like this: statement ::= (simple_stmt (NEWLINE | ; stmt_list NEWLINE | local_namespace) ) | (compound_stmt [local_namespace]) local_namespace ::= with : suite Corrected version of the above to avoid an unintended syntax change: statement ::= (simple_stmt (NEWLINE | ; [stmt_list] NEWLINE | local_namespace) ) | (compound_stmt [local_namespace]) local_namespace ::= with : suite (The original version incorrectly prohibited a trailing semi-colon for a single statement) Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://boredomandlaziness.skystorm.net -- http://mail.python.org/mailman/listinfo/python-list
Re: Statement local namespaces summary (was Re: python3: 'where' keyword)
+1 I really like the idea of this. It removes all my objections to lambdas, and replaces it with something clear and readable. I look forward to seeing what comes of it. Anna -- http://mail.python.org/mailman/listinfo/python-list