[Python-checkins] r83002 - peps/trunk/pep-3150.txt

nick.coghlan python-checkins at python.org
Wed Jul 21 00:08:46 CEST 2010


Author: nick.coghlan
Date: Wed Jul 21 00:08:46 2010
New Revision: 83002

Log:
Updates based on python-ideas discussion

Modified:
   peps/trunk/pep-3150.txt

Modified: peps/trunk/pep-3150.txt
==============================================================================
--- peps/trunk/pep-3150.txt	(original)
+++ peps/trunk/pep-3150.txt	Wed Jul 21 00:08:46 2010
@@ -1,5 +1,5 @@
 PEP: 3150
-Title: Statement local namespaces (aka "where" clause)
+Title: Statement local namespaces (aka "given" clause)
 Version: $Revision$
 Last-Modified: $Date$
 Author: Nick Coghlan <ncoghlan at gmail.com>
@@ -15,7 +15,7 @@
 Abstract
 ========
 
-A recurring proposal on python-ideas is the addition of some form of
+A recurring proposal ([1], [2], [3]) on python-ideas is the addition of some form of
 statement local namespace.
 
 This PEP is intended to serve as a focal point for those ideas, so we
@@ -41,7 +41,7 @@
 Proposal
 ========
 
-This PEP proposes the addition of an optional "where" clause to the
+This PEP proposes the addition of an optional ``given`` clause to the
 syntax for simple statements which may contain an expression. The
 current list of simple statements that would be affected by this
 addition is as follows:
@@ -55,49 +55,107 @@
 * raise statement
 * assert statement
 
-The ``where`` clause would allow subexpressions to be referenced by
+The ``given`` clause would allow subexpressions to be referenced by
 name in the header line, with the actual definitions following in
 the indented clause. As a simple example::
 
- c = sqrt(a*a + b*b) where:
+ c = sqrt(a*a + b*b) given:
     a = retrieve_a()
     b = retrieve_b()
 
-Torture Test
-============
-
-An implementation of this PEP must support execution of the following
-code at module, class and function scope::
-
-  b = {}
-  a = b[f(a)] = x where:
-    x = 42
-    def f(x):
-      return x
-  assert "x" not in locals()
-  assert "f" not in locals()
-  assert a == 42
-  assert d[42] == 42 where:
-    d = b
-  assert "d" not in locals()
-
-Most naive implementations will choke on the first complex assignment,
-while less naive but still broken implementations will fail when
-the torture test is executed at class scope.
-
-And yes, that's a perfectly well-defined assignment statement. Insane,
-I agree, but legal::
-
-  >>> def f(x): return x
-  ... 
-  >>> x = 42
-  >>> b = {}
-  >>> a = b[f(a)] = x
-  >>> a
-  42
-  >>> b
-  {42: 42}
+Rationale
+=========
 
+Some past language features (specifically function decorators
+and list comprehensions) were motivated, at least in part, by
+the desire to give the important parts of a statement more
+prominence when reading code. In the case of function decorators,
+information such as whether or not a method is a class or static
+method can now be found in the function definition rather than
+after the function body. List comprehensions similarly take the
+expression being assigned to each member of the list and move it
+to the beginning of the expression rather than leaving it buried
+inside a ``for`` loop.
+
+The rationale for the ``given`` clause is similar. Currently,
+breaking out a subexpression requires naming that subexpression
+*before* the actual statement of interest. The ``given`` clause
+is designed to allow a programmer to highlight for the reader
+the statement which is actually of interest (and presumably has
+significance for later code) while hiding the most likely irrelevant
+"calculation details" inside an indented suite.
+
+Using the simple example from the proposal section, the current Python
+equivalent would be::
+
+  a = retrieve_a()
+  b = retrieve_b()
+  c = sqrt(a*a + b*b)
+
+If later code is only interested in the value of c, then the
+details involved in retrieving the values of a and b may be an
+unnecessary distraction to the reader (particularly if those
+details are more complicated than the simple function calls
+shown in the example).
+
+To use a more illustrative example (courtesy of Alex Light),
+which of the following is easier to comprehend?
+
+Subexpressions up front?::
+
+  sea = water()
+  temp = get_temperature(sea)
+  depth = get_depth(sea)
+  purity = get_purity(sea)
+  saltiness = get_salinity(sea)
+  size = get_size(sea)
+  density = get_density(sea)
+  desired_property = calc_value(temp, depth, purity,
+                                salinity, size, density)
+  # Further operations using desired_property
+
+Or subexpressions indented?::
+
+  desired_property = calc_value(temp, depth, purity,
+                                salinity, size, density) given:
+    sea = water()
+    temp = get_temperature(sea)
+    depth = get_depth(sea)
+    purity = get_purity(sea)
+    saltiness = get_salinity(sea)
+    size = get_size(sea)
+    density = get_density(sea)
+  # Further operations using desired_property
+
+The ``given`` clause may also function as a more readable
+alternative to some uses of lambda expressions and similar
+constructs when passing one-off functions to operations
+like ``sorted``.
+
+One way to think of the proposed clause is as a middle
+ground between normal in-line code and separation of an
+operation out into a dedicated function.
+
+Keyword Choice
+==============
+
+This proposal initially used ``where`` based on the name of a similar
+construct in Haskell. However, it has been pointed out that there
+are existing Python libraries (such as Numpy [4]) that already use
+``where`` in the SQL query condition sense, making that keyword choice
+potentially confusing.
+
+While ``given`` may also be used as a variable name (and hence would be
+deprecated using the usual ``__future__`` dance for introducing
+new keywords), it is associated much more strongly with the desired
+"here are some extra variables this expression may use" semantics
+for the new clause.
+
+Reusing the ``with`` keyword has also been proposed. This has the
+advantage of avoiding the addition of a new keyword, but also has
+a high potential for confusion as the ``with`` clause and ``with``
+statement would look similar but do completely different things.
+That way lies C++ and Perl :)
 
 Syntax Change
 =============
@@ -117,12 +175,12 @@
 
   expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
                ('=' (yield_expr|testlist_star_expr))*) [where_clause]
-  del_stmt: 'del' exprlist [where_clause]
-  return_stmt: 'return' [testlist] [where_clause]
-  yield_stmt: yield_expr [where_clause]
-  raise_stmt: 'raise' [test ['from' test]] [where_clause]
-  assert_stmt: 'assert' test [',' test] [where_clause]
-  where_clause: "where" ":" suite
+  del_stmt: 'del' exprlist [given_clause]
+  return_stmt: 'return' [testlist] [given_clause]
+  yield_stmt: yield_expr [given_clause]
+  raise_stmt: 'raise' [test ['from' test]] [given_clause]
+  assert_stmt: 'assert' test [',' test] [given_clause]
+  given_clause: "given" ":" suite
 
 (Note that expr_stmt in the grammar covers assignment and augmented
 assignment in addition to simple expression statements)
@@ -145,20 +203,22 @@
 non-trivial restructuring of the grammar, breaking up small_stmt and
 flow_stmt to separate the statements that potentially contain arbitrary
 subexpressions and then allowing a single one of those statements with
-a ``where`` clause at the simple_stmt level. Something along the lines of::
+a ``given`` clause at the simple_stmt level. Something along the lines of::
 
+  stmt: simple_stmt | given_stmt | compound_stmt
   simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
-  where_stmt: subexpr_stmt (where_clause | (';' small_stmt)* [';']) NEWLINE
-  subexpr_stmt: expr_stmt | del_stmt | flow_subexpr_stmt | assert_stmt
   small_stmt: (pass_stmt | flow_stmt | import_stmt |
                global_stmt | nonlocal_stmt)
   flow_stmt: break_stmt | continue_stmt
+  given_stmt: subexpr_stmt (given_clause |
+                (';' (small_stmt | subexpr_stmt))* [';']) NEWLINE
+  subexpr_stmt: expr_stmt | del_stmt | flow_subexpr_stmt | assert_stmt
   flow_subexpr_stmt: return_stmt | raise_stmt | yield_stmt
-  where_clause: "where" ":" suite
-
+  given_clause: "given" ":" suite
 
 For reference, here are the current definitions at that level::
 
+  stmt: simple_stmt | compound_stmt
   simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
   small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
                import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
@@ -177,19 +237,29 @@
   jump around a little strangely, as the body of the ``where``
   clause is executed before the simple statement in the clause
   header. The closest any other part of Python comes to this
-  before is the out of order evaluation in conditional
-  expressions.
-  
+  is the out of order evaluation in list comprehensions,
+  generator expressions and conditional expressions.
+
+These objections should not be dismissed lightly - the proposal
+in this PEP needs to be subjected to the test of application to
+a large code base (such as the standard library) in a search
+for examples where the readability of real world code is genuinely
+enhanced.
+
+New PEP 8 guidelines would also need to be developed to provide
+appropriate direction on when to use the ``given`` clause over
+ordinary variable assignments.
 
 Possible Additions
 ==================
 
-* The current proposal allows the addition of a ``where`` clause only
+* The current proposal allows the addition of a ``given`` clause only
   for simple statements. Extending the idea to allow the use of
   compound statements would be quite possible, but doing so raises
-  serious readability concerns (as values defined in the ``where``
+  serious readability concerns (as values defined in the ``given``
   clause may be used well before they are defined, exactly the kind
-  of readability trap that decorators were designed to eliminate)
+  of readability trap that other features like decorators and ``with``
+  statements are designed to eliminate)
 
 * Currently only the outermost clause of comprehensions and generator
   expressions can reference the surrounding namespace when executed
@@ -197,7 +267,43 @@
   associated namespace semantics could allow that restriction to be
   lifted. There would be backwards compatibility implications in doing
   so as existing code may be relying on the behaviour of ignoring
-  class level variables.
+  class level variables, but the idea is worth considering.
+
+Torture Test
+============
+
+An implementation of this PEP must support execution of the following
+code at module, class and function scope::
+
+  b = {}
+  a = b[f(a)] = x given:
+    x = 42
+    def f(x):
+      return x
+  assert "x" not in locals()
+  assert "f" not in locals()
+  assert a == 42
+  assert d[42] == 42 given:
+    d = b
+  assert "d" not in locals()
+
+Most naive implementations will choke on the first complex assignment,
+while less naive but still broken implementations will fail when
+the torture test is executed at class scope.
+
+And yes, that's a perfectly well-defined assignment statement. Insane,
+you might rightly say, but legal::
+
+  >>> def f(x): return x
+  ... 
+  >>> x = 42
+  >>> b = {}
+  >>> a = b[f(a)] = x
+  >>> a
+  42
+  >>> b
+  {42: 42}
+
 
 Possible Implementation Strategy
 ================================
@@ -256,6 +362,9 @@
 
 .. [2] http://mail.python.org/pipermail/python-ideas/2010-July/007584.html
 
+.. [3] http://mail.python.org/pipermail/python-ideas/2009-July/005132.html
+
+.. [4] http://mail.python.org/pipermail/python-ideas/2010-July/007596.html
 
 Copyright
 =========


More information about the Python-checkins mailing list