python3: 'where' keyword

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Nick Coghlan

    #46
    Re: Statement local namespaces summary (was Re: python3: 'where'keyword)

    Bengt Richter wrote:[color=blue]
    > Problems? (Besides NIH, which I struggle with regularly, and had to overcome to accept Tim's
    > starting point in this ;-)[/color]

    The ideas regarding creating blocks whose name bindings affect a different scope
    are certainly interesting (and relevant to the 'using' out-of-order execution
    syntax as well).

    Out-of-order execution appeals to me, but the ability to flag 'hey, this is just
    setup for something I'm doing later' might be a reasonable alternative
    (particularly with the affected names highlighted on the first line). As Jeff
    pointed out, it would be significantly less surprising for those encountering
    the construct for the first time. Folding code editors would be able to keep the
    setup clause out of the way if you really wanted to hide it.

    On the other hand, it might be feasible to construct a virtually identical
    out-of-order two suite syntax, similar to the mathematical phrasing "let f =
    c/lambda where f is the frequency, c is the speed of light and lambda is the
    wavelength". Either way, you've convinced me that two suites (and a new compound
    statement), as well as specifying which names can be rebound in the containing
    scope, is a better way to go than trying to mess with the definition of Python
    statements.

    On keywords, while 'let' is nice for assignments, I find it just doesn't parse
    properly when I put function or class definitions in the clause. So, I'll swap
    it for 'use' in the examples below. The statement could then be read "use these
    outer bindable names, and this additional code, in this suite". YMMV, naturally.

    Let's consider some of the examples given for 'where' using an in-order let/in
    type syntax (the examples only bind one name at a time, but would allow multiple
    names):

    # Anonymous functions
    use res:
    def f(x):
    d = {}
    exec x in d
    return d
    in:
    res = [f(i) for i in executable]

    # Declaring properties
    class C(object):
    use x:
    def get(self):
    print "Demo default"
    def set(self, value):
    print "Demo default set"
    in:
    x = property(get, set)

    # Design by contract
    use foo:
    def pre():
    pass
    def post():
    pass
    in:
    @dbc(pre, post)
    def foo():
    pass

    # Singleton classes
    use C:
    class _C:
    pass
    in:
    C = _C()

    # Complex default values
    use f:
    def default():
    return "Demo default"
    in:
    def f(x=default()):
    pass

    They actually read better than I expected. Nicely, the semantics of this form of
    the syntax *can* be articulated cleanly with current Python:

    use <names>: <use-suite>
    in: <in-suite>

    as equivalent to:

    def __use_stmt():
    <use-suite>
    def _in_clause():
    <in-suite>
    return <names>
    return _in_clause()
    __use_stmt_args = {}
    <names> = __use_stmt()
    del __use_stmt

    Those semantics don't allow your switch statement example, though, since it
    doesn't use any magic to write to the outer scope - it's just a normal return
    and assign.

    However, I don't think starting with these semantics would *preclude* adding the
    ability to name the second block at a later date, and make the name rebinding
    part of executing that block - the standard usage doesn't really care *how* the
    names in the outer scope get bound, just so long as they do. Whether I think
    that's a good idea or not is an entirely different question :)

    Another aspect to consider is whether augmented assignment operations in the
    inner-scopes should work normally - if so, it would be possible to alter the
    semantics to include passing the existing values as arguments to the inner scopes.

    Moving on to considering a two-suite out-of-order syntax, this would have
    identical semantics to the above, but a syntax that might look something like:

    as <names>: <in-suite>
    using: <use-suite>

    # Anonymous functions
    as res:
    res = [f(i) for i in executable]
    using:
    def f(x):
    d = {}
    exec x in d
    return d

    # Declaring properties
    class C(object):
    as x:
    x = property(get, set)
    using:
    def get(self):
    print "Demo default"
    def set(self, value):
    print "Demo default set"

    # Design by contract
    as foo:
    @dbc(pre, post)
    def foo():
    pass
    using:
    def pre():
    pass
    def post():
    pass

    # Singleton classes
    as C:
    C = _C()
    using:
    class _C:
    pass

    # Complex default values
    as f:
    def f(x=default()):
    pass
    using:
    def default():
    return "Demo default"

    Cheers,
    Nick.

    --
    Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
    ---------------------------------------------------------------

    Comment

    • Nick Coghlan

      #47
      Re: Statement local namespaces summary (was Re: python3: 'where'keyword)

      Nick Coghlan wrote:[color=blue]
      > as equivalent to:
      >
      > def __use_stmt():
      > <use-suite>
      > def _in_clause():
      > <in-suite>
      > return <names>
      > return _in_clause()
      > __use_stmt_args = {}
      > <names> = __use_stmt()
      > del __use_stmt
      >[/color]

      The more I think about this return-based approach, the less I like it. It could
      probably be made to work, but it just feels like a kludge to work around the
      fact that the only mechanisms available for altering the bindings of local names
      are assignment and definition statements.

      For class namespaces, getattr(), setattr() and delattr() work a treat, and
      globals() works fine for module level name binding.

      locals() is an unfortunate second class citizen, since it writes to it aren't
      propagated back to the executing frame. Programmatic interrogation of locals is
      fine, but update is impossible.

      What would be interesting is if locals() returned a dictionary whose __setitem__
      method invoked PyFrame_LocalsT oFast on the relevant frame, instead of a vanilla
      dictionary as it does now.

      Then locals()["x"] = foo would actually work properly.

      Notice that you can get this effect today, by using exec to force invocation of
      PyFrame_LocalsT oFast:

      Py> def f():
      .... n = 1
      .... def g(outer=locals( )):
      .... outer["n"] += 1
      .... g() # Does not affect n
      .... print n
      .... exec "g()" # DOES affect n
      .... print n
      ....
      Py> f()
      1
      2

      (The call to g() has to be inside the exec statement, since the exec statement
      evaluation starts with a call to PyFrame_FastToL ocals).

      Assuming a writeable locals(), the semantics for the normal case are given by:
      ============
      def __use_stmt(__ou ter):
      <use-suite>
      <in-suite>
      __inner = locals()
      for name in <names>:
      __outer[name] = __inner[name]

      __use_stmt(loca ls())
      del __use_stmt
      ============

      And for the 'delayed execution' case:
      ============
      def __named_use_stm t(__outer):
      <use-suite>
      def __delayed_block ():
      <in-suite>
      __inner = locals()
      for name in <names>:
      __outer[name] = __inner[name]

      return __delayed_block

      <in-name> = __named_use_stm t(locals())
      del __named_use_stm t
      ============

      Cheers,
      Nick.

      --
      Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
      ---------------------------------------------------------------

      Comment

      • Andrey Tatarinov

        #48
        Re: Statement local namespaces summary (was Re: python3: 'where'keyword)

        Nick Coghlan wrote:[color=blue]
        > # Anonymous functions
        > use res:
        > def f(x):
        > d = {}
        > exec x in d
        > return d
        > in:
        > res = [f(i) for i in executable][/color]

        as for me, I found construction "use <name>:" unobvious and confusing.
        Also there is great possibility to forget some of variables names.

        I think that syntax

        <block>
        where:
        <block>

        is more obvious. (and we already have defined semantics for it)

        we have two problems, that we try to solve
        1) create method to nest scopes
        2) create method to reverse execution order for better readability

        "using:" solves both at once.
        but your "use ... in ..." syntax shows, that you want to be able to
        solve 1) independently i.e. create nested scope without reversing
        execution order.

        so, I can suggest one more keyword "do:", which will create nested
        scope, just as "def f(): ... ; f()" do (and that could be just syntaxic
        sugar for it.

        so "use ... in ..." would look the following way:

        do:
        res = [f(i) for i in executable]
        #some more equations here
        using:
        def f(x):
        d = {}
        exec x in d
        return d

        that seems good for me. of course if you want to return something from
        the nest scope you must show that variable is from parent scope.

        // while writing that I realized that it's too complex to be implemented
        in python in that way. consider it as some type of brainstorming.

        Comment

        Working...