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