PEP-308 a "simplicity-first" alternative

Steven Taschuk staschuk at telusplanet.net
Thu Feb 13 06:44:43 EST 2003


Quoth Erik Max Francis:
  [on Bengt Richter's {x} proposal]
> So can you rigorously define the behavior of an expression with {}
> markers in the general case?  Can you even rigorously define it here? 
> What happens when it appears somewhere other than one of the operands to
> a logical operator, what then?  Can you even fully define its semantics?

As I read Richter's proposal, {expr} would be an object such as
	class braced:
		def __nonzero__(self):
			return 1
		def __getattr__(self, name):
			return (lambda: expr)().__getattr__(name)
		def __setattr__(self, name, value):
			return (lambda: expr)().__setattr__(name, value)
That is, an object which delegates to the value of expr for
everything except __nonzero__.  (One outstanding question in this
interpretation is whether, e.g., {expr}.__class__ should refer to
expr's class or the braced object's.)

Note that expr above is not a variable named 'expr', but a
metasyntactic variable for an arbitrary expression.  The lambdas
thus accomplish lazy evaluation, whence the desired short-
circuiting behaviour in the context "x and {y} or z".  As written
above they also allow {expr} to change value:
	>>> x = 3
	>>> y = {x+1}
	>>> print y
	4
	>>> x += 1
	>>> print y
	5
Memoizing the result of (lambda: expr)() would avoid this, but
we'd still get:
	>>> x = 3
	>>> y = {x+1}
	>>> x += 1
	>>> print y
	5
(Since, by design, expr would not be evaluated until the first
invocation of a method other than __nonzero__.)

If memory serves, Richter mentioned that 'or' would "unwrap" the
braced object, presumably to prevent it from being passed around
the program and wreaking havoc.  But if used in contexts other
than the and/or ternary conditional idiom, braced values introduce
fragility into all sorts of code:
	x = []
	y = { map(somefunction, x) }
	# ... 50 lines of code fiddling with x, occasionally
	#     looking at y to find out what x's data maps to
	#     under somefunction ...
	if y:              # intended to mean "if y is nonempty"
		print y[0] # possible IndexError
(This example is not entirely silly: it could be rewritten with "y
= lambda: map(somefunction, x)", and "y()" instead of "y"
thereafter, but the coder might prefer these newfangled braces for
their terseness.)

Many of these complexities can be avoided by permitting these
braces only in unwrapping contexts.  In this scenario, they're
much more like an inflection of those contexts than a way of
creating an object with a weird truth value (since the putative
object would only ever be accessible to the unwrapping context). 
Then the fact that the braces look like some kind of operation on
or container for the enclosed expression is misleading, and a
separate, more perspicuous, syntax for those contexts would be
preferable.

That leaves us back where we started, seeking a clear syntax for
the ternary conditional.

-- 
Steven Taschuk           | Aral: "Confusion to the enemy, boy."
staschuk at telusplanet.net | Mark: "Turn-about is fair play, sir."
                         | (_Mirror Dance_, Lois McMaster Bujold)





More information about the Python-list mailing list