Re: [Python-Dev] PEP 3103: A Switch/Case Statement

2006-06-29 Thread Eric Sumner
> yeah, but what are they?  integers?  strings?  names without an associated 
> value?
> how do you create new labels?  where are they stored?  who keeps track of 
> them?

In this scheme, dispatch tables can be considered to be reverse-lookup
namespaces.  Where a regular namespace is used to look up a value
given its name, a dispatch table is used to look up a name given its
value.  The switch statement then lets you actually do something based
on which name is returned.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 3103: A Switch/Case Statement

2006-06-29 Thread Eric Sumner
> >> what's a "label" ?
> >
> > In your example, RED, GREEN, and BLUE.  colours provides a mapping
> > from values to labels/cases, and the switch statement provides a
> > mapping from labels/cases to code.  Sorry about introducing a new term
> > without saying anything about it.
>
> yeah, but what are they?  integers?  strings?  names without an associated 
> value?
Syntactically, they are bare words (names).  They are constants, and
compare equal to other identical labels.

> how do you create new labels?
To the programmer, all valid labels exist; you just use them.  They
are only used in very particular places in the grammar.  Internally,
they are probably represented by strings.

> where are they stored?
They are stored by the internal representation of any construct that
uses them.  That would be dispatch table objects and compiled switch
statements.

> who keeps track of them?
Each construct keeps track of its own copies, and destroys them when
they are no longer needed.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 3103: A Switch/Case Statement

2006-06-29 Thread Eric Sumner
> >> You mean something like this?:
> >>
> >>switch x in colours:
> >>  case RED:
> >>  # whatever
> >>  case GREEN:
> >>  # whatever
> >>  case BLUE:
> >>  # whatever
> >>
> >> I think Guido's right. It doesn't solve the underlying problem because the
> >> compiler still has to figure out how to build a dispatch table from the
> >> possible values in colours to the actual bytecode offsets of the cases.
> >
> > To implement this, you actually need two lookup tables: one particular
> > to the switch that maps labels to bytecode offsets, and one in the
> > dispatch table to map values to labels.  The former is built when the
> > switch is compiled, and the latter is built wherever the dispatch
> > table is defined.  Each lookup is still O(1), so the whole operation
> > remains O(1).
>
> what's a "label" ?

In your example, RED, GREEN, and BLUE.  colours provides a mapping
from values to labels/cases, and the switch statement provides a
mapping from labels/cases to code.  Sorry about introducing a new term
without saying anything about it.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 3103: A Switch/Case Statement

2006-06-29 Thread Eric Sumner
On 6/29/06, Nick Coghlan <[EMAIL PROTECTED]> wrote:
> You mean something like this?:
>
>switch x in colours:
>  case RED:
>  # whatever
>  case GREEN:
>  # whatever
>  case BLUE:
>  # whatever
>
> I think Guido's right. It doesn't solve the underlying problem because the
> compiler still has to figure out how to build a dispatch table from the
> possible values in colours to the actual bytecode offsets of the cases.

To implement this, you actually need two lookup tables: one particular
to the switch that maps labels to bytecode offsets, and one in the
dispatch table to map values to labels.  The former is built when the
switch is compiled, and the latter is built wherever the dispatch
table is defined.  Each lookup is still O(1), so the whole operation
remains O(1).

It is O(n) or worse to check that all of the cases in the switch are
defined in the dispatch table, but that only has to be done once per
dispatch table/switch statement pair, and can then be stred in one or
the other (probably the dispatch table, as that will be a proper
object).

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 3103: A Switch/Case Statement

2006-06-28 Thread Eric Sumner
> > Forget subroutines for a moment - the main point of the thread was the
> > idea that the dispatch table was built explicitly rather than
> > automatically - that instead of arguing over first-use vs.
> > function-definition, we let the user decide. I'm sure that my specific
> > proposal isn't the only way that this could be done.
>
> But anything that makes the build explicit is going to be so much more
> ugly. And I still think you're trying to solve the wrong problem.

Only if the programmer has to see it.  The dispatch table need not
include the behaviors of each of the cases; it only needs to define
what the cases are.  In most of the use cases I've seen, switch is
used to define behavior for different values of an enumeration.  The
dispatch table for an enumeration can be built wherever the values for
the enumeration are defined (such as in a module).  Programmers don't
need to bother with making a dispatch table unless they are defining
enumeration values themselves.

  -- Eric Sumner

Note: I sent an email yesterday with a proposal to this effect, but it
seems to have been lost.  If anybody wants, I can resend it.
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] Split switch statement

2006-06-27 Thread Eric Sumner
One of the big problems here seems to be that an optimized switch
statement requires some code to be evaluated fewer times than the rest
of the switch statement, and there isn't any good way to make that
happen with a single statement.  Thus, I propose two statements: a
'dispatcher' statement and a 'switch' statement.  The dispatcher
statement defines the available cases, and generates a dispatcher
object, and the switch statement specifies code to be run for each
case.

-

#Sample 1: Primary dispatcher syntax
dispatcher myEvents on e:
on e:
 case None: Idle
 on getattr(e, 'type', None):
 from globalEvents: *
 case 42: myEvent  # or whatever the user event code is
#EOF

A dispatcher statement contains a sequence of 'on' blocks.  Each 'on'
block specifies an expression and a set of cases.  The expression is
stored as a lambda inside the dispatcher which is applied whenever the
switch is run.  Inside a 'on' block, there are two kinds of
statements.  'case' evaluates its expression immediately, and
associates it with a label; 'from' imports tests and labels from
another dispatcher.  If the result of any case expression is
unhashable, an exception is raised.

--

#Sample 2: Shorthand dispatcher syntax
dispatcher globalEvents:
case pygame.KEYDOWN: KEYDOWN
case pygame.KEYUP:   KEYUP
...
#EOF

Because dispatching on the switched value directly is so common, any
'from' or 'case' statements outside an 'on' block are considered to be
applied to be inside an "on " block.  The name for the
switched value can be omitted if it's not needed.

--
#Sample 3: Using a dispatcher
while True:
...
switch events on pygame.event.poll():
case KEYUP, KEYDOWN: ...
case myEvent: ...
case Idle: ...
else: ...
#EOF

Internally, each switch statement has some unique identifier.  Each
dispatcher object maintains a list of the switch statements it has
previously serviced.  If this switch statement is new to this
dispatcher, the dispatcher verifies that it might generate all of the
cases that are specified in the switch.  Otherwise, an exception is
raised.

If the test passed (or was skipped due to previous experience), each
of the 'on' expressions in the dispatcher is executed (in order) and
their results are checked against the stored values.  If no case (from
the switch, not the dispatcher) matches, the switch's 'else' block is
executed, if present.  If more than one case (from the switch)
matches, an exception is raised.  Otherwise, the code from associated
case block is executed.

  -- Eric Sumner

PS. Yes, I know that's not how pygame handles idle events; it makes a
better sample this way.
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Temporary Constantification

2006-06-25 Thread Eric Sumner
On 6/25/06, Guido van Rossum <[EMAIL PROTECTED]> wrote:
> Unfortunately, a mechanism that would let you register a callback for
> when a particular variable or attribute used in a cached expression is
> used, is pretty hard to implement without affecting the performance of
> code that doesn't use it. I'm afraid this is not a very likely path
> towards a solution.

I could make a strong argument that it is actually impossible to
implement without affecting the performance of other code; the only
issue is whether or not the impact is acceptable.  I may be wrong, but
I think that this particular scheme minimizes the impact:
  - There is a bit more data to store in every namespace
  - There is no change to dereferencing names; no test is required, no
callback is generated
  - Binding to a name that currently has no binding simply requires
allocating the extra memory and clearing it.
  - Binding to a name that is bound and does have callbacks is slow,
but those are supposed to be constant *in practice* anyway.
  - Binding to a name that is already bound, but has no callbacks
requires a test on a single variable against a constant.

Without knowing more about the internals of Python (such as how long a
check of a single variable takes relative to binding a new value to a
name), I can't properly evaluate how much of a problem this would be.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] Temporary Constantification

2006-06-25 Thread Eric Sumner
It seems to me that the main reason to provide constantification (in a
switch statement or anywhere else) is to be able to optimize execution
by caching the result locally for later use.  The problem comes from
trying to determine exactly when and how a value gets calculated, as
most values in Python are subject to change.

If, however, there was a mechanism by which a cache could be
invalidated when its value changes, the only remaining difference
between cached and non-cached values becomes their execution speed and
memory usage (and possibly impact on the execution speed of other
code).  Thus, I propose a 'cached' keyword much like the static and
const proposed keywords.

In general, a cached value can be used (rather than re-evaluating the
expression) if:
  - The expression has no side effects,
  - The result of all operations is deterministic, and
  - None of the expression parameters have changed since the cached
value was generated

The first two can be determined at compile-time without too much
difficulty (except possibly function calls, I'll get to those in a
minute).  The hard issue here is knowing when parameters have changed.
 These fall into two different categories: literals and name lookups.
Immutable literals don't cause a problem, and mutable literals always
have a side-effect of generating a new object.  There are two ways to
determine if name lookups have changed:
  1) Cache all the parameters, and check them against the current values, or
  2) Be notified whenever one of the parameters changes.

The first option requires a bunch of name lookups whenever the cached
value is considered, which is exactly the problem that caching is
supposed to fix.  To implement the second, each name in each namespace
needs a list of caches that depend on the name, and all name binding
operations need to check the list and mark all dependent caches
invalid.  This is a small performance penalty whenever any name is
rebound, and a large penalty whenever a watched name is rebound.

Function calls can safely be considered volatile, but this would
invalidate many of the uses for caching.  Instead, each function has
an immutable property of being either volatile or deterministic.  Each
deterministic function call maintains its own cache which is
invalidated if  the name to which the associated function (or any of
its parameters) is rebound.  Thus, if a function is rebound to
something volatile, it does not force continual re-evaluation of other
sub-expressions.  Functions should be assumed to be volatile unless
specified otherwise (probably via a decorator).

I'm not particularly familiar with the internals of Python, so I'm not
able to actually assess the feasability or performance implications of
this proposal, but I think it basically covers the problem.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Switch statement

2006-06-23 Thread Eric Sumner
> > In that case, I would argue that the proposed syntax is misleading.
> > Regardless of how it is implemented, a switch statement is
> > conceptually a chain of if/elif statements.  As such, the 'in'
> > keyword, if it is allowed at all, should behave like it does in if
> > statements, rather than it does in loops.  If, for implementation
> > reasons, you want to ensure that all of the sets are enumerable, I
> > would recommend a syntax like this:
> >
> >"case" ["*"] expression ("," ["*"] expression)* ":" suite
> >
> > This is consistent with parameter lists, which emphasizes that the
> > sequences are being enumerated instead of simply tested against.
>
> You apparently missed the post where Guido expressed that he believes
> that one of the valid motivators for the switch statement and the
> dict-based dispatching was for that of speed improvements.  He also
> already stated that cases could essentially only be examples for which
> Python does pre-computation and the storing of constants (he didn't use
> those words, and there are caveats with regards to module.attr and
> global 'constants', but that was the gist I got from it).

I admit that I came into this discussion in the middle, and my initial
post was for informational (to me) purposes only.  I did not mean to
imply by that post that the proposal was flawed in any way, just to
verify that I properly understood the proposal.  I am sorry if I was
unclear about this.

> As such, because any explicit range object is neither dict-accessable as
> the values in the range would be, nor are they generally precomputed (or
> precomputable) as constants (like (1,2,3) is and 1+1 should be), your
> particular use-case (range objects that may implement __contains__ fast,
> but whose __iter__ returns a huge number of values if it were
> implemented as such) is not covered under switch/case, and we would
> likely point you back off to if/elif/else.

I concur.  I actually suspected as much prior to my original message
on this topic, but I wanted to make sure I was understanding things
correctly before attempting to make a suggestion.

> This is a good thing, because if switch/case ends up functionally
> identical to if/elif/else, then it has no purpose as a construct.  On
> the other hand, because it is different from if/elif/else, and it is
> different in such a way to make certain blocks of code (arguably) easier
> to read or understand, (likely provably) faster, then it actually has a
> purpose and use.

Again, I concur.  My point was not that the mechanics of the construct
were incorrect, but that the proposed syntax misrepresented its
function.  Again, I am sorry if I was unclear about this.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Switch statement

2006-06-23 Thread Eric Sumner
On 6/23/06, Guido van Rossum <[EMAIL PROTECTED]> wrote:
> No; in order to make it possible to use a single dict lookup for
> dispatch, the set members are expanded into the dict key. If you have
> a large contiguous range, you'll be better off (sometimes *much*
> better) doing an explicit if/elif check before entering the switch.

In that case, I would argue that the proposed syntax is misleading.
Regardless of how it is implemented, a switch statement is
conceptually a chain of if/elif statements.  As such, the 'in'
keyword, if it is allowed at all, should behave like it does in if
statements, rather than it does in loops.  If, for implementation
reasons, you want to ensure that all of the sets are enumerable, I
would recommend a syntax like this:

   "case" ["*"] expression ("," ["*"] expression)* ":" suite

This is consistent with parameter lists, which emphasizes that the
sequences are being enumerated instead of simply tested against.

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Switch statement

2006-06-23 Thread Eric Sumner
On 6/22/06, Guido van Rossum <[EMAIL PROTECTED]> wrote:
> (3) A switch is implemented using a dict which is precomputed at the
> same time its static expressions are precomputed. The switch
> expression must be hashable. Overlap between different cases will
> raise an exception at precomputation time.

How does this interact with __contains__, __len__, and __iter__ for
the 'case in S' statement?  Would it work with a class that only
implements __contains__, such as a continuous range class?

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 343: Context managers a superset of decorators?

2006-02-13 Thread Eric Sumner
On 2/13/06, Nick Coghlan <[EMAIL PROTECTED]> wrote:
> Eric Sumner wrote:
> > I realize that I made an assumption that may not be valid;
> > namely, that a new scope is generated by the 'with' statement.
>
> The with statement uses the existing scope - its just a way of factoring out
> try/finally boilerplate code. No more, and, in fact, fractionally less (the
> 'less' being the fact that just like any other Python function, you only get
> to supply one value to be bound to a name in the invoking scope).

Ok.  These changes are more substantial than I thought, then.

> Trying to link this with the function definition pipelining provided by
> decorators seems like a bit of a stretch. It certainly isn't a superset of the
> decorator functionality - if you want a statement that manipulates the
> namespace it contains, that's what class statements are for :)

Several examples of how the 'with' block would be used involve
transactions which are either rolled back or confirmed.  All of these
use the transaction capabilities of some external database.  With
separate scopes, the '__exit__' function can decide which names to
export outwards to the containing scope.  Unlike class statements, the
contained scope is used temporarily and can be discarded when the
'with' statement is completed.  This would allow a context manager to
provide a local transaction handler.

To me, it is not much of a leap from copying data between scopes to
modifying it as it goes through, which is exactly what decorators do. 
The syntax that this provides for decorators seems reasonable enough
(to me) to make the '@' syntax redundant.  However, this is a larger
change than I thought, and maybe not worth the effort to implement.

  -- Eric
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 343: Context managers a superset of decorators?

2006-02-13 Thread Eric Sumner
On 2/12/06, Josiah Carlson <[EMAIL PROTECTED]> wrote:
[paragraphs swapped]
> The desire for context managers to have access to its enclosing scope is
> another discussion entirely, though it may do so without express
> permission via stack frame manipulation.

My main point was that, with relatively small changes to 343, it can
replace the decorator syntax with a more general solution that matches
the style of the rest of the language better.  The main change (access
to scopes) makes this possible, and the secondary change (altering the
block syntax) mitigates (but does not remove) the syntax difficulties
presented.  I realize that I made an assumption that may not be valid;
namely, that a new scope is generated by the 'with' statement.  Stack
frame manipulation would not be able to provide access to a scope that
no longer exists.

> Re-read the decorator PEP: http://www.python.org/peps/pep-0318.html to
> understand why both of these options (indentation and prefix notation)
> are undesireable for a general decorator syntax.

With the changes that I propose, both syntaxes are equivalent and can
be used interchangeably.  While each of them has problems, I believe
that in situations where one has a problem, the other usually does
not.

>From this point on, I provide a point-by-point reaction to the most
applicable syntax objections listed in PEP 318.  If you're not
interested in this, bail out now.

In the PEP, there is no discussion of a prefix notation in which the
decorator is placed before the 'def' on the same line.  The most
similar example has the decorator between the 'def' and the parameter
list.  It mentions two problems:

> There are a couple of objections to this form. The first is that it breaks
> easily 'greppability' of the source -- you can no longer search for 'def foo('
> and find the definition of the function. The second, more serious, objection
> is that in the case of multiple decorators, the syntax would be extremely
> unwieldy.

The first does not apply, as this syntax does not separate 'def' and
the function name.  The second is still a valid concern, but the
decorator list can easily be broken across multiple lines.

The main objection to an indented syntax seems to be that it requires
decorated functions to be indented an extra level.  For simple
decorators, the compacted syntax could be used to sidestep this
problem.  The main complaints about the J2 proposal don't quite apply:
the code in the block is a sequence of statements and 'with' is
already going to be added to the language as a compound statement.

  -- Eric
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] PEP 343: Context managers a superset of decorators?

2006-02-12 Thread Eric Sumner
Forgive me if someone has already come up with this; I know I am
coming to the party several months late.  All of the proposals for
decorators (including the accepted one) seemed a bit kludgey to me,
and I couldn't figure out why.  When I read PEP 343, I realized that
they all provide a solution for an edge case without addressing the
larger problem.

If context managers are provided access to the contained and
containing namespaces of their with statement, they can perform the
same function that decorators do now.  A transforming class could be
implemented as:

## Code Start -
class DecoratorContext(object):
def __init__(self, func): self.func = func
def __context__(self): return self
def __enter__(self, contained, containing): pass
def __exit__(self, contained, containing):
for k,v in contained.iteritems():
containing[k] = self.func(v)
## Code End ---

With this in place, decorators can be used with the with statement:

## Code Start -
classmethod = DecoratorContext(classmethod)

class foo:
def __init__(self, ...): pass
with classmethod:
def method1(cls, ...):
pass
def method2(cls, ...):
pass
## Code End ---

The extra level of indention could be avoided by dealing with multiple
block-starting statements on a line by stating that all except the
last block contain only one statement:

## Code Start -
classmethod = DecoratorContext(classmethod)

class foo:
def __init__(self, ...): pass
with classmethod: def method1(cls, ...):
pass
with classmethod: def method2(cls, ...):
pass
## Code End ---

I will readily admit that I have no idea how difficult either of these
suggestions would be to implement, or if it would be a good idea to do
so.  At this point, they are just something to think about

  -- Eric Sumner
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com