Re: Statement local namespaces summary (was Re: python3: 'where' keyword)

2005-01-14 Thread Nick Coghlan
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)

2005-01-13 Thread Nick Coghlan
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)

2005-01-13 Thread Jeff Shannon
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)

2005-01-13 Thread Bengt Richter
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)

2005-01-13 Thread Nick Coghlan
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)

2005-01-11 Thread Nick Coghlan
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)

2005-01-11 Thread Andrey Tatarinov
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)

2005-01-11 Thread Nick Coghlan
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)

2005-01-11 Thread Nick Coghlan
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)

2005-01-11 Thread Nick Coghlan
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)

2005-01-11 Thread Andrey Tatarinov
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)

2005-01-10 Thread Nick Coghlan
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)

2005-01-10 Thread Nick Coghlan
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)

2005-01-10 Thread Anna
+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