pre-PEP: Suite-Based Keywords - syntax proposal
Bengt Richter
bokr at oz.net
Sat Apr 16 21:10:47 EDT 2005
On 16 Apr 2005 09:07:09 -0700, "Kay Schluehr" <kay.schluehr at gmx.net> wrote:
>The idea is interesting but not unambigously realizable. Maybe one
>should introduce some extra syntax for disambiguation and thereby
>generalize the proposal.
This is intriguing. I am reminded of trailing "where x is something" phrase, but with
an implied x.
>
>as <specifier>:
> # list of definitions and assignments
ISTM this list, other than for func, always could be considered to
generate an ordered sequence of (key, value) pairs that need to be plugged into
the preceding context. Where to plug it in is implicit, but it could be given
explicit target name(s) in an expression, e.g.,
<expr> where: <where-suite>
and <where-suite> would be a series of assignments like for suite-based keywords, except that
the key names would be used in resolving the names in the expression preceding the where:, e.g.,
foo(x, y) where:
x = 1
y = 2
would make the foo call using the where bindings by name, here effectively calling foo(1, 2)
But sometimes we want all the bindings in a suite in a chunk, as with suite-based keyword bindings.
I'm proposing below a unary suite operator spelled '::' which will return a single tuple of 2-tuples
representing the ordered bindings produced by the statement suite following the '::' Thus ::<suite>
is a general expression that can be used anywhere an expression can be used. (I'll explain indentation rules).
The "::" expression I'm proposing generalizes capturing suite bindings into an ordered sequence of (key,value)
tuples, like an ordered vars().items() limited to the bindings produced in the suite following "::"
Thus
items = ::
x = 1
y = [1,2]
def foo():pass
print items => (('x', 1), ('y', [1, 2]), ('foo', <function foo at 0x02EE8D14>))
Now we can use a :: expression in a where: spec, e.g.
d = dict(items) where:
items = ('added','value') + :: # note that :: is a unary suite operator producing a tuple of tuples
x = 1
y = [1,2]
def foo():pass
where: actually introduces a suite of scoped assignments much like :: e.g.,
d = {key:value} where: key='x'; value='123' # where is ;-greedy on the same line
print d => {'x':123}
or a multi-parameter call
foo(x, y=23, *args, **kw) where:
x = 333; y = 555
args = [44, 55, 66]
kw = dict(::
z = 'zee'
class C(object): pass
c = C()
del C )
resulting in foo being called like foo(333, 555, 44, 55, 66, z='zee', c=C()) # except C was transiently defined
You can also use a :: expression directly, skipping the where: reference resolution mechanism, e.g.,
d = dict(::
x = 1
y = [1,2]
def foo():pass) # closing bracket not opened in :: suite ends the suite like a sufficient dedent would
It can also participate as a normal expression anywhere a tuple of tuples could go, e.g.
print list(t[0] for t in ::
x = 1
y = [1,2]
def foo():pass)
=> ['x', 'y', 'foo']
Now we can rewrite some examples. Thanks for the inspiration to generalize/orthogonalize ;-)
>
>Proposed specifiers are dict, tuple, *, ** and func.
>
>
>- as dict:
>
> conversion into a dictionary
>
> Example:
>
> d = as dict:
> doc = "I'm the 'x' property."
> def fget(self):
> return self.__x
d = dict(items) where:
items = ::
doc = "I'm the 'x' property."
def fget(self):
return self.__x
Or more concisely
d = dict(::
doc = "I'm the 'x' property."
def fget(self):
return self.__x )
>
> We would get d = {"doc":"I'm the 'x' property.", "fget":fget}
>
>
>- as **:
>
> conversion into keyword-arguments. This comes close in spirit to the
> original proposal
BTW, this would happen to cover dict alternatively by way of your format
d = dict():
as **: key=value #etc
>
> Example:
>
> x = property():
> as **:
> doc = "I'm the 'x' property."
> def fget(self):
> return self.__x
>
x = property(**kw) where:
kw = dict(::
doc = "I'm the 'x' property."
def fget(self):
return self.__x)
or
x = property(fget=fget, doc=doc) where:
doc = "I'm the 'x' property."
def fget(self):
return self.__x
>- as tuple:
>
> conversion into a tuple. Preserving order.
>
> Example:
>
> t = as tuple:
> doc = "I'm the 'x' property."
> def fget(self):
> return self.__x
> >>> t[1]
> <function fget at 0x00EC4770>
"as tuple:" is special sugar for
t = tuple(*v) where:
v = (t[1] for t in ::
doc = "I'm the 'x' property."
def fget(self):
return self.__x )
>
>
>- as *:
>
> conversion into an argument tuple. Preserving order.
That's the same as passing values to tuple above
>
> Example:
>
> x = property():
> as *:
> def get_x():
> return self.__x
> def set_x(value):
> self.__x = value
> del_x = None
> doc = "I'm the 'x' property."
x = property(*seq) where:
seq = (item[1] for item in ::
def get_x():
return self.__x
def set_x(value):
self.__x = value
del_x = None
doc = "I'm the 'x' property." )
>
>
>
>- as func(*args,**kw):
>
> Anoymus functions. Replacement for lambda. Support for
> arbirtray statements?
I prefer dropping combinations of def and/or <name> from def <name>(<arglist>): <suite>
to get
def <name>(<arglist>): <suite> # normal def
def (<arglist>): <suite> # anonymous def
<name>(<arglist>): <suite> # named callable suite, aka named thunk
(<arglist>): <suite> # anonymous callable suite, aka thunk
any of which can be passed to a decorator or bound or passed to functions etc.
The parsing of indentation for the three last definitions is like for the :: unary suite operator
i.e., they are all expressions, not statements, and ::<suite> is an expression that ends where the
<suite> ends, so there are some rules. The first is that a closing bracket not opened within the suite
ends the suite. This is typically the closing paren of a function call, but could be anything, e.g.,
print [:: x=123; y=456] => [(('x', 123), ('y', 456))] # ie the :: value is a single tuple of tuples.
More typical would be the use of def(<arglist>):<suite> as a substitute for lambda <arglist>:<expr>
BTW, note that :: is ;-greedy in a single-line multi-statement suite, so
items = :: x=1; y=2 # => (('x',1), ('y',2))
I.e., it has the effect of
items = ::
x = 1
y = 2
Not
items == ::
x = 1
y = 2
The same greediness goes for "where:"
>
> Examples:
>
> p = as func(x,y): x+y
> p(1,2)
p = def(x, y): x+y
>
> i,j = 3,4
> if (as func(x,y): x+y) (i,j) > 10:
> print True
if (def(x,y): x+y) (i,j) > 10:
print true
I haven't thought of all the uses for anonymous callable suites (thunks), but imagine as callback:
(putting it as an unusual default parameter makes it bind in the same namspace as the definintion of foo)
def foo(x, cb=(y):print y):
cb(x)
foo(3) # => prints 3 and assigns y=3 as local-to-foo-definer side effect (often main effect)
foo(123, (z):print 'Setting z=%r'%z) => prints Setting z=123 and sets z=123 locally to foo caller.
foo(456, lambda *ignore:None) # also legal, foo doesn't know what callable it's being passed
The suite-based keyword call syntax
foo():
x = 1
y = [1,2]
def foo():pass
can be handled with the more general
foo(**kw) where: kw = dict(::
x = 1
y = [1,2]
def foo():pass)
This is a new rev on my thoughts. Expect contradictions with older stuff.
Naturally I like my newest ideas best ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list