Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > python3: 'where' keyword

Reply
Thread Tools

python3: 'where' keyword

 
 
Nick Coghlan
Guest
Posts: n/a
 
      01-13-2005
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 | http://www.velocityreviews.com/forums/(E-Mail Removed) | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net
 
Reply With Quote
 
 
 
 
Jeff Shannon
Guest
Posts: n/a
 
      01-13-2005
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.
>


 
Reply With Quote
 
 
 
 
Andrey Tatarinov
Guest
Posts: n/a
 
      01-13-2005
Nick Coghlan 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


as for me, I would expect following:

1 {'a': 1, 'b': 2}
2 {'a': 2, 'b': 2, 'c': 3'}
3 {'a': 2, 'b': 2, 'c': 3'}
4 {'a': 1, 'b': 2}

otherwise that would be impossible to do calculations based on scope
variables and "using:" would be useless =), consider example of usage:

current_position = 1
current_environment # = ...
current_a_lot_of_other_parameters # = ...
scores = [count_score(move) for move in aviable_moves] using:
def count_score(move):
#walking through current_environment
return score
 
Reply With Quote
 
Bengt Richter
Guest
Posts: n/a
 
      01-13-2005
On Thu, 13 Jan 2005 22:41:54 +0300, Andrey Tatarinov <(E-Mail Removed)> wrote:

>Nick Coghlan 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

>
>as for me, I would expect following:
>
>1 {'a': 1, 'b': 2}
>2 {'a': 2, 'b': 2, 'c': 3'}
>3 {'a': 2, 'b': 2, 'c': 3'}
>4 {'a': 1, 'b': 2}


locals() doesn't automatically show everything that is visible from its local scope:

>>> def foo():

... a = 1
... b = 2
... print 1, locals()
... def inner():
... a = 2
... c = 3
... print 2, locals()
... inner()
... print 4, locals()
...
>>> foo()

1 {'a': 1, 'b': 2}
2 {'a': 2, 'c': 3}
4 {'a': 1, 'b': 2, 'inner': <function inner at 0x02EE8BFC>}

-- unless you actually use it (this is a bit weird maybe?):

>>> def foo():

... a = 1
... b = 2
... print 1, locals()
... def inner():
... a = 2
... c = 3
... print 2, locals(), 'and b:', b
... inner()
... print 4, locals()
...
>>> foo()

1 {'a': 1, 'b': 2}
2 {'a': 2, 'c': 3, 'b': 2} and b: 2
4 {'a': 1, 'b': 2, 'inner': <function inner at 0x02EE8D4C>}

of course a difference using the new syntax is that 'inner' is not bound to a persistent name.

>
>otherwise that would be impossible to do calculations based on scope
>variables and "using:" would be useless =), consider example of usage:
>
>current_position = 1
>current_environment # = ...
>current_a_lot_of_other_parameters # = ...
>scores = [count_score(move) for move in aviable_moves] using:
> def count_score(move):
> #walking through current_environment
> return score

No worry, UIAM.

Regards,
Bengt Richter
 
Reply With Quote
 
Bengt Richter
Guest
Posts: n/a
 
      01-13-2005
On Fri, 14 Jan 2005 01:48:48 +1000, Nick Coghlan <(E-Mail Removed)> 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=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 writes through to foo with the def, as permitted by let(foo).

Problems? (Besides NIH, which I struggle with regularly, and had to overcome to accept Tim's
starting point in this

Regards,
Bengt Richter
 
Reply With Quote
 
Nick Coghlan
Guest
Posts: n/a
 
      01-14-2005
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 | (E-Mail Removed) | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net
 
Reply With Quote
 
Nick Coghlan
Guest
Posts: n/a
 
      01-14-2005
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 | (E-Mail Removed) | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net
 
Reply With Quote
 
Andrey Tatarinov
Guest
Posts: n/a
 
      01-16-2005
Nick Coghlan wrote:
> # Anonymous functions
> use res:
> def f(x):
> d = {}
> exec x in d
> return d
> in:
> res = [f(i) for i in executable]


as for me, I found construction "use <name>:" unobvious and confusing.
Also there is great possibility to forget some of variables names.

I think that syntax

<block>
where:
<block>

is more obvious. (and we already have defined semantics for it)

we have two problems, that we try to solve
1) create method to nest scopes
2) create method to reverse execution order for better readability

"using:" solves both at once.
but your "use ... in ..." syntax shows, that you want to be able to
solve 1) independently i.e. create nested scope without reversing
execution order.

so, I can suggest one more keyword "do:", which will create nested
scope, just as "def f(): ... ; f()" do (and that could be just syntaxic
sugar for it.

so "use ... in ..." would look the following way:

do:
res = [f(i) for i in executable]
#some more equations here
using:
def f(x):
d = {}
exec x in d
return d

that seems good for me. of course if you want to return something from
the nest scope you must show that variable is from parent scope.

// while writing that I realized that it's too complex to be implemented
in python in that way. consider it as some type of brainstorming.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
RE: keyword checker - keyword.kwlist Hamilton, William Python 4 05-13-2007 06:31 AM
keyword checker - keyword.kwlist tom@finland.com Python 6 05-10-2007 04:53 PM
Like keyword Patrick ASP .Net 1 01-26-2005 01:25 AM
Re: Why does using keyword require a new scope? Jip from Paris ASP .Net 0 08-25-2003 08:02 AM
Re: Why does using keyword require a new scope? Herman Eldering ASP .Net 0 08-23-2003 11:32 PM



Advertisments