RE: pre-PEP: Suite-Based Keywords - syntax proposal

Discussion in 'Python' started by Robert Brewer, Apr 17, 2005.

  1. Bengt Richter wrote:
    > The '::' unary suite operator should return an ordered dict
    > subtype representing the bindings


    Why ordered?


    Robert Brewer
    MIS
    Amor Ministries
     
    Robert Brewer, Apr 17, 2005
    #1
    1. Advertising

  2. Robert Brewer

    Kay Schluehr Guest

    Robert Brewer wrote:
    > Bengt Richter wrote:
    > > The '::' unary suite operator should return an ordered dict
    > > subtype representing the bindings

    >
    > Why ordered?


    Because You can't otherwise guarantee to feed optional argument
    parameters in a correct way.

    Example:

    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." )

    This statement would not work if the result of '::' ( considered as an
    "ordered dict" ) would return simply a dict because the order of the
    input parameters matters.

    Ciao,
    Kay
     
    Kay Schluehr, Apr 17, 2005
    #2
    1. Advertising

  3. On 16 Apr 2005 23:43:03 -0700, "Kay Schluehr" <> wrote:

    >
    >Robert Brewer wrote:
    >> Bengt Richter wrote:
    >> > The '::' unary suite operator should return an ordered dict
    >> > subtype representing the bindings

    >>
    >> Why ordered?

    >
    >Because You can't otherwise guarantee to feed optional argument
    >parameters in a correct way.
    >
    >Example:
    >
    >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." )
    >
    >This statement would not work if the result of '::' ( considered as an
    >"ordered dict" ) would return simply a dict because the order of the
    >input parameters matters.
    >

    Exactly. Except the above example is from the day-old-bread items-tuple-returning version of :: ;-)
    And with an ordered dict subtype there is no need for the generator expression either,
    since there is a values method for dicts (which in the subtype would preserve order). E.g.,

    x = property(*seq) where:
    seq = :):
    def get_x():
    return self.__x
    def set_x(value):
    self.__x = value
    del_x = None
    doc = "I'm the 'x' property." ).values())

    Or more directly:

    x = property(*:):
    def get_x():
    return self.__x
    def set_x(value):
    self.__x = value
    del_x = None
    doc = "I'm the 'x' property." ).values())

    Or eliminating parens by using dedent to end the :: suite:

    x = property(*::
    def get_x():
    return self.__x
    def set_x(value):
    self.__x = value
    del_x = None
    doc = "I'm the 'x' property."
    .values())

    Regards,
    Bengt Richter
     
    Bengt Richter, Apr 17, 2005
    #3
  4. Robert Brewer

    Kay Schluehr Guest

    > Exactly. Except the above example is from the day-old-bread
    items-tuple-returning version of :: ;-)
    > And with an ordered dict subtype there is no need for the generator

    expression either,
    > since there is a values method for dicts (which in the subtype would

    preserve order). E.g.,
    >
    > x = property(*seq) where:
    > seq = :):
    > def get_x():
    > return self.__x
    > def set_x(value):
    > self.__x = value
    > del_x = None
    > doc = "I'm the 'x' property." ).values())
    >
    > Or more directly:
    >
    > x = property(*:):
    > def get_x():
    > return self.__x
    > def set_x(value):
    > self.__x = value
    > del_x = None
    > doc = "I'm the 'x' property." ).values())


    Hmmm ... now You eliminate "where" completely in favor for '::'. This
    may be reasonable because '::' is stronger and less context dependent.
    But on the other hand it may be also reasonable to eliminate '::'
    towards a stronger "where" ;)


    x = property(**kw) where kw:
    doc = "I'm the 'x' property."
    def fget(self):
    return self.__x


    x = property(*args) where args:
    def fget(self):
    return self.__x
    fset = None
    fdel = None
    doc = "I'm the 'x' property."


    Put definitions into a list:

    l = list(*args) where args:
    def fget(self):
    return self.__x
    doc = "I'm the 'x' property."


    Nest suites:

    x = property(*args) where args:
    t = tuple(*t) where t:
    def fget(self):
    return self.__x
    fset = None
    fdel = None
    doc = "I'm the 'x' property."


    Evaluate conditions:

    if f(*args)==1 where args:
    x = 1
    y = 2

    I think this version is more mainstream syntax ( and without braces and
    additional punctuation ) than the unary prefix operator '::' which
    drops statements into expressions within expressions.

    Regards,
    Kay
     
    Kay Schluehr, Apr 17, 2005
    #4
  5. On 17 Apr 2005 09:27:34 -0700, "Kay Schluehr" <> wrote:

    >> Exactly. Except the above example is from the day-old-bread

    >items-tuple-returning version of :: ;-)
    >> And with an ordered dict subtype there is no need for the generator

    >expression either,
    >> since there is a values method for dicts (which in the subtype would

    >preserve order). E.g.,
    >>
    >> x = property(*seq) where:
    >> seq = :):
    >> def get_x():
    >> return self.__x
    >> def set_x(value):
    >> self.__x = value
    >> del_x = None
    >> doc = "I'm the 'x' property." ).values())
    >>
    >> Or more directly:
    >>
    >> x = property(*:):
    >> def get_x():
    >> return self.__x
    >> def set_x(value):
    >> self.__x = value
    >> del_x = None
    >> doc = "I'm the 'x' property." ).values())

    >
    >Hmmm ... now You eliminate "where" completely in favor for '::'. This
    >may be reasonable because '::' is stronger and less context dependent.
    >But on the other hand it may be also reasonable to eliminate '::'
    >towards a stronger "where" ;)
    >
    >
    >x = property(**kw) where kw:
    > doc = "I'm the 'x' property."
    > def fget(self):
    > return self.__x
    >
    >
    >x = property(*args) where args:
    > def fget(self):
    > return self.__x
    > fset = None
    > fdel = None
    > doc = "I'm the 'x' property."
    >

    I like this. But how would you put "where args:" and "where kw:" if you needed both?
    also, is it looking back to see the '*' or '**' to do :):x=1).values vs. :):x=1)
    and how about :):x=1).keys() or :):x=1).items() ? And what if you wanted to pass
    :):x=1) as a dict object without ** expansion into a keyword dict?

    Maybe we need asterisks on both ends. e.g.,

    foo(dct, values, *args, **kw):
    where **dct:
    x=1
    where *values:
    x=2
    where *args:
    x=3
    where **kw:
    x=4

    But that still doesn't give you, e.g.,

    foo(keys) where:
    keys=sorted(:):
    from interesting.module import *
    ).keys())

    I like clean sugar, but I still want to be able to
    get at the general primitives to compose them in ways
    that can't be anticipated until a use case comes up.
    And then if the primitives are inaccessible, one is
    out of luck or doomed to workaround hacking ;-)

    >
    >Put definitions into a list:
    >
    >l = list(*args) where args:
    > def fget(self):
    > return self.__x
    > doc = "I'm the 'x' property."
    >
    >
    >Nest suites:
    >
    >x = property(*args) where args:
    > t = tuple(*t) where t:
    > def fget(self):
    > return self.__x
    > fset = None
    > fdel = None
    > doc = "I'm the 'x' property."
    >
    >
    >Evaluate conditions:
    >
    >if f(*args)==1 where args:
    > x = 1
    > y = 2
    >
    >I think this version is more mainstream syntax ( and without braces and
    >additional punctuation ) than the unary prefix operator '::' which
    >drops statements into expressions within expressions.
    >

    I like this mainstream syntax for ordinary use cases, but as mentioned,
    I'd still like to have primitives accessible. I don't see why both
    couldn't live in harmony ;-)

    Regards,
    Bengt Richter
     
    Bengt Richter, Apr 18, 2005
    #5
  6. Kay Schluehr wrote:
    > Hmmm ... now You eliminate "where" completely in favor for '::'. This
    > may be reasonable because '::' is stronger and less context dependent.
    > But on the other hand it may be also reasonable to eliminate '::'
    > towards a stronger "where" ;)
    >
    > x = property(**kw) where kw:
    > doc = "I'm the 'x' property."
    > def fget(self):
    > return self.__x
    >
    >
    > x = property(*args) where args:
    > def fget(self):
    > return self.__x
    > fset = None
    > fdel = None
    > doc = "I'm the 'x' property."

    [snip]
    > I think this version is more mainstream syntax ( and without braces and
    > additional punctuation ) than the unary prefix operator '::' which
    > drops statements into expressions within expressions.


    So the object of a "where" is then always an ordered dict? If so, then
    I guess I like this proposal best so far.

    However, it does seem to have the problem that you can't have any
    additional local variables so, for example, list comprehensions are
    probably not usable...

    Or can you still drop the argument to "where" and just use the names
    directly? E.g.:

    x = property(fget=fget, doc=doc) where:
    doc = "I'm the 'x' property."
    def fget(self):
    return self.__x

    STeVe
     
    Steven Bethard, Apr 18, 2005
    #6
  7. Robert Brewer

    Kay Schluehr Guest

    Bengt Richter wrote:
    > On 17 Apr 2005 09:27:34 -0700, "Kay Schluehr" <>

    wrote:
    >
    > >> Exactly. Except the above example is from the day-old-bread

    > >items-tuple-returning version of :: ;-)
    > >> And with an ordered dict subtype there is no need for the

    generator
    > >expression either,
    > >> since there is a values method for dicts (which in the subtype

    would
    > >preserve order). E.g.,
    > >>
    > >> x = property(*seq) where:
    > >> seq = :):
    > >> def get_x():
    > >> return self.__x
    > >> def set_x(value):
    > >> self.__x = value
    > >> del_x = None
    > >> doc = "I'm the 'x' property." ).values())
    > >>
    > >> Or more directly:
    > >>
    > >> x = property(*:):
    > >> def get_x():
    > >> return self.__x
    > >> def set_x(value):
    > >> self.__x = value
    > >> del_x = None
    > >> doc = "I'm the 'x' property." ).values())

    > >
    > >Hmmm ... now You eliminate "where" completely in favor for '::'.

    This
    > >may be reasonable because '::' is stronger and less context

    dependent.
    > >But on the other hand it may be also reasonable to eliminate '::'
    > >towards a stronger "where" ;)
    > >
    > >
    > >x = property(**kw) where kw:
    > > doc = "I'm the 'x' property."
    > > def fget(self):
    > > return self.__x
    > >
    > >
    > >x = property(*args) where args:
    > > def fget(self):
    > > return self.__x
    > > fset = None
    > > fdel = None
    > > doc = "I'm the 'x' property."
    > >

    > I like this. But how would you put "where args:" and "where kw:" if

    you needed both?
    > also, is it looking back to see the '*' or '**' to do :):x=1).values

    vs. :):x=1)
    > and how about :):x=1).keys() or :):x=1).items() ? And what if you

    wanted to pass
    > :):x=1) as a dict object without ** expansion into a keyword dict?
    >
    > Maybe we need asterisks on both ends. e.g.,
    >
    > foo(dct, values, *args, **kw):
    > where **dct:
    > x=1
    > where *values:
    > x=2
    > where *args:
    > x=3
    > where **kw:
    > x=4


    Yes... Why not?

    >
    > But that still doesn't give you, e.g.,
    >
    > foo(keys) where:
    > keys=sorted(:):
    > from interesting.module import *
    > ).keys())


    This particular statement won't work anyway inside a where-clause
    because
    "from *" must be called from module level. You would have to import
    interesting.module before:

    import interesting.module
    foo(keys) where:
    keys = sorted(interesting.module.__dict__).keys()

    But it wasn't ever intended to put arbitrary statements in a kw-suite,
    right?

    > I like clean sugar, but I still want to be able to
    > get at the general primitives to compose them in ways
    > that can't be anticipated until a use case comes up.
    > And then if the primitives are inaccessible, one is
    > out of luck or doomed to workaround hacking ;-)


    You can always consider "where" as function of a statement. The only
    restriction You have to make is to bind "where" to a function-call i.e.
    regard each function as a function object with a where() method.


    f(*args,**kw ).where( <specifier>,
    (<assignment-statement>|
    <function-definition>|
    <class-definition>)*
    )

    But that is not a loss of generality because a free :): x=1 ) can be
    mapped onto

    dict(**kw).where(**kw, x=1)

    and that is

    dict(**kw) where **kw:
    x=1

    I don't see any case where this translation fails. Only if it comes to
    functional composition like

    f(g(...(h:): x=1)...))

    it may be awkward to expand this into a nested where clauses. You might
    probably define the argument not in a suite ;)

    Regards,
    Kay
     
    Kay Schluehr, Apr 18, 2005
    #7
  8. Robert Brewer

    Kay Schluehr Guest

    Steven Bethard wrote:

    > So the object of a "where" is then always an ordered dict?


    Yes.

    > If so, then
    > I guess I like this proposal best so far.
    >
    > However, it does seem to have the problem that you can't have any
    > additional local variables so, for example, list comprehensions are
    > probably not usable...
    >
    > Or can you still drop the argument to "where" and just use the names
    > directly? E.g.:
    >
    > x = property(fget=fget, doc=doc) where:
    > doc = "I'm the 'x' property."
    > def fget(self):
    > return self.__x


    I can't see why this shouldn't work?

    The specifiers behind "where" are present to determine the matching
    behaviour. The order of a dict is caused by different specifiers i.e. a
    dict- or tuple-like specifier. If a specifier is not present only names
    can be matched regardless of a sequence and this is always possible
    because we still have a dict with names as keys.

    What should not be possible are statements like this:

    x = property(a, b) where:
    doc = "I'm the 'x' property."
    def fget(self):
    return self.__x

    because there is no rule to match doc and fget onto a and b. In this
    case we would need specifiers:

    x = property(a, b)
    where **a:
    doc = "I'm the 'x' property."
    where **b:
    def fget(self):
    return self.__x

    Ciao,
    Kay
     
    Kay Schluehr, Apr 18, 2005
    #8
  9. On 17 Apr 2005 21:48:47 -0700, "Kay Schluehr" <> wrote:
    <snip>
    >> >

    >> I like this. But how would you put "where args:" and "where kw:" if

    >you needed both?
    >> also, is it looking back to see the '*' or '**' to do :):x=1).values

    >vs. :):x=1)
    >> and how about :):x=1).keys() or :):x=1).items() ? And what if you

    >wanted to pass
    >> :):x=1) as a dict object without ** expansion into a keyword dict?
    >>
    >> Maybe we need asterisks on both ends. e.g.,
    >>
    >> foo(dct, values, *args, **kw):
    >> where **dct:
    >> x=1
    >> where *values:
    >> x=2
    >> where *args:
    >> x=3
    >> where **kw:
    >> x=4

    >
    >Yes... Why not?


    On second thought, "where **x:" might as well be "where x:" since ** is a good default,
    but I think "where x:" looks too much like and ordinary suite will follow, rather than
    capturing the bindings created by the suite and binding that dict to x temporarily.
    I would rather see "where x::" to indicate that, which also ties in consistently with
    my syntax for for thunks as suite expressions, in that that now gives named def-like
    statement versions to all the anonymous versions, when you include this <name>::<suite>
    along with <name>(<thunkarglist>):<thunksuite> and <name> def(<defarglist>):<anonymous_def_suite>

    Of course that last is equivalent to an ordinary def, and I can't see using it, since a def will
    do everywhere except immediately following where, and the assignment form is clearer there, e.g.:

    foo(123) where foo=def(x):
    print x

    as a way to define a transient function and call it


    so with that in mind, and I just realized we can do away with * specifiers
    if we move that job to where the dict of bindings is used:

    foo(dct, values.values(), *args.values(), **kw): #XXX ':' used as function call trailer not good
    where dct:: # get suite as dict in all cases
    x=1
    where values::
    x=2
    where args::
    x=3
    where kw::
    x=4

    Now using the same syntax to define temporarily named thunks for the arg list:

    thunk_accepter(tk1, tk2, [1,2], **kw) where: # now where: can introduce an ordinary where suite
    tk1(i):
    print i
    tk2(j):
    rejectlist.append(j)
    kw::
    x = 1
    y = 2

    This would be equivalent to

    thunk_accepter(tk1, tk2, [1,2], **kw) where:
    tk1 = (i):
    print i
    tk2 = (j):
    rejectlist.append(j)
    kw = ::
    x = 1
    y = 2

    Which might look more "mainstream", and be ok for multiple values, but for a single thunk,
    which is probably the most frequent use case, a statement should be able to
    end in "where <single assignment or name binder>:" e.g.,

    foo(x) where x=1 # minimal silly

    or named thunk definition syntax, which binds the name and uses a suite, e.g.,

    safe_open(tk, filepath, filemode) where tk(f):
    for line in f:
    print line[:40]

    would be nice and concise. And it looks pretty mainstream, in that
    tk(f) looks just like the thunk call that safe_open will do to run the suite
    in the local namespace. Might even look good to Brian? ;-)

    But note that "where tk(f):<suite>" is really equivalent to the assignment form
    "where tk = (f):<suite>" much like def foo(): return 'hi' is equivalent to foo = lambda: 'hi'
    (and my thunk definition syntax derives from def foo(<arglist>):<suite> minus "def foo" ;-)

    >
    >>
    >> But that still doesn't give you, e.g.,
    >>
    >> foo(keys) where:
    >> keys=sorted(:):
    >> from interesting.module import *
    >> ).keys())

    >
    >This particular statement won't work anyway inside a where-clause
    >because
    >"from *" must be called from module level. You would have to import
    >interesting.module before:
    >
    > import interesting.module
    > foo(keys) where:
    > keys = sorted(interesting.module.__dict__).keys()
    >
    >But it wasn't ever intended to put arbitrary statements in a kw-suite,
    >right?

    Sure, as arbitrary as inside a function. Import * was a bad example. I never use that,
    so I shouldn't have used it in an example, even though on my system it does it, grudgingly:

    >>> def foo():

    ... from math import *
    ... return vars().keys()
    ...
    <stdin>:1: SyntaxWarning: import * only allowed at module level
    >>> foo()

    ['pow', 'cosh', 'ldexp', 'hypot', 'tan', 'asin', 'log', 'fabs', 'floor', 'sqrt', 'frexp', 'degre
    es', 'pi', 'log10', 'sin', 'modf', 'atan', 'ceil', 'sinh', 'cos', 'e', 'tanh', 'radians', 'atan2
    ', 'fmod', 'exp', 'acos']
    >>>


    I should have put a safe import inside, using your example
    foo(keys) where:
    import interesting.module
    keys = sorted(interesting.module.__dict__).keys()

    If you import it outside of that scope, you will have 'interesting' bound outside.

    I'm not saying go wild and do weird things, I'm just saying don't impose arbitrary
    restrictions on adults. These explorations are just to illustrate possibilities,
    not to advocate them all as good conventional use.

    >
    >> I like clean sugar, but I still want to be able to
    >> get at the general primitives to compose them in ways
    >> that can't be anticipated until a use case comes up.
    >> And then if the primitives are inaccessible, one is
    >> out of luck or doomed to workaround hacking ;-)

    >
    >You can always consider "where" as function of a statement. The only
    >restriction You have to make is to bind "where" to a function-call i.e.
    >regard each function as a function object with a where() method.


    Um, I think that's too narrow for where. Consider

    foo = f1; bar=f2; x=k1; y=k2
    foo(x)*bar(y)[3].attr

    now should

    foo(x)*bar(y)[3].attr where:
    foo = f1; bar=f2; x=k1; y=k2

    act any different? How do you spell this as a where bound to a function call?

    >
    >
    >f(*args,**kw ).where( <specifier>,
    > (<assignment-statement>|
    > <function-definition>|
    > <class-definition>)*
    > )
    >
    >But that is not a loss of generality because a free :): x=1 ) can be
    >mapped onto
    >
    > dict(**kw).where(**kw, x=1)
    >
    >and that is
    >
    > dict(**kw) where **kw:
    > x=1



    so for
    foo(*args) where:
    args = dict(**kw).keys() where **kw:
    x=1

    but it's much easier (now with explorations and ideas above ;-) to write

    foo(*args.values()) where args::
    x=1

    >
    >I don't see any case where this translation fails. Only if it comes to
    >functional composition like
    >
    > f(g(...(h:): x=1)...))
    >
    >it may be awkward to expand this into a nested where clauses. You might
    >probably define the argument not in a suite ;)


    There are probably some things that won't look reasonable no matter what ;-)

    But to summarize, I think we should try to write a real grammar for all this,
    and show how it would fit in with the real python grammar.

    I think I should make a separate post for this ;-)
    see
    grammar for where/letting/with and suite expressions (thunks etc)


    Regards,
    Bengt Richter
     
    Bengt Richter, Apr 19, 2005
    #9
  10. Robert Brewer

    Kay Schluehr Guest

    Bengt Richter wrote:

    [...]

    > Um, I think that's too narrow for where. Consider
    >
    > foo = f1; bar=f2; x=k1; y=k2
    > foo(x)*bar(y)[3].attr
    >
    > now should
    >
    > foo(x)*bar(y)[3].attr where:
    > foo = f1; bar=f2; x=k1; y=k2


    I think we are diverging again. You are right with Your objection about
    my claim of generality but I'm completely against the latter statement
    not only because

    foo(x) where:
    foo = f1

    is not much better than

    foo(x) where:
    def foo():
    # do some stuff

    which should clearly raise an exception and be abandoned.

    After all I start backtracking: the purpose of defining suites is still
    prevention of namespaces pollution with helper-functions not a sake of
    it's own and not inventing of a suite-based programming style for
    everything. The examples You presented have become almost pathological
    examples of what should be prevented and where syntax cannot be rigid
    enough.

    So there are following requierements about we seem to agree:

    - define suites on a functions-call scope in order to define helper
    functions
    that would otherwise pollute external namespaces

    - mark the func-call-scope by some keyword either "with" or "where"
    because
    it has to be separated from the calling environment both for
    consistency
    and user friendlyness

    - Simple matching rules for both keyword-parameters and
    argument-tuples.
    Make explicit which kind of inference rule is selected.

    I think that my last proposal I finally distilled from our discussion
    would pretty much fullfill all three requirements whereas that of
    Andrey Tatarinov would be somewhat more restrictive and fullfills only
    the first two.

    Ciao,
    Kay
     
    Kay Schluehr, Apr 19, 2005
    #10
  11. On 19 Apr 2005 00:16:32 -0700, "Kay Schluehr" <> wrote:

    >
    >Bengt Richter wrote:
    >
    >[...]
    >
    >> Um, I think that's too narrow for where. Consider
    >>
    >> foo = f1; bar=f2; x=k1; y=k2
    >> foo(x)*bar(y)[3].attr
    >>
    >> now should
    >>
    >> foo(x)*bar(y)[3].attr where:
    >> foo = f1; bar=f2; x=k1; y=k2

    >
    >I think we are diverging again. You are right with Your objection about
    >my claim of generality but I'm completely against the latter statement
    >not only because
    >
    >foo(x) where:
    > foo = f1
    >
    >is not much better than
    >
    >foo(x) where:
    > def foo():
    > # do some stuff
    >
    >which should clearly raise an exception and be abandoned.


    Why do you say that? foo(x) is just a call of whatever foo is bound to.
    The where: suite supplies a transient namespace where foo happens to
    be defined in the conventional way. This allows definition and call of
    foo without binding it in the caller's local namespace, much like
    (lambda(): do_some_stuff)()

    IMO that's a perfectly valid use. You don't have to worry that your
    def foo():... will clobber some other foo.

    >
    >After all I start backtracking: the purpose of defining suites is still
    >prevention of namespaces pollution with helper-functions not a sake of
    >it's own and not inventing of a suite-based programming style for
    >everything. The examples You presented have become almost pathological
    >examples of what should be prevented and where syntax cannot be rigid
    >enough.

    You don't have to write what you consider to be pathological, but
    this becomes sort of a free speech issue. I.e., don't impose a style-censoring
    spell-checker on me please ;-)

    >
    >So there are following requierements about we seem to agree:
    >
    >- define suites on a functions-call scope in order to define helper
    >functions
    > that would otherwise pollute external namespaces

    Why just function call? You realize that a "function call" is just the
    effect of (<arglist>) tacked on the tail of an expression that is not
    necessarily just a name. E.g., notice the possibilities the parser goes
    through before finding that foo() is a simple name expression with a () trailer:

    [ 1:18] C:\pywk\parse\ast>py24 debg.py -full foo()
    'file_input'
    'stmt'
    'simple_stmt'
    'small_stmt'
    'expr_stmt'
    'testlist'
    'test'
    'and_test'
    'not_test'
    'comparison'
    'expr'
    'xor_expr'
    'and_expr'
    'shift_expr'
    'arith_expr'
    'term'
    'factor'
    'power'
    'atom'
    NAME 'foo'
    'trailer'
    LPAR '('
    RPAR ')'
    NEWLINE ''
    ENDMARKER ''

    What about instance.method(args) as a call? Should you not be
    able write
    instance.method(*args) where:
    args = []
    # append various values to args here,
    # in single statements and/or using looping etc.

    to specify complex args in a where? That looks like

    [ 2:18] C:\pywk\parse\ast>py24 debg.py -full instance.method(*args)
    'file_input'
    'stmt'
    'simple_stmt'
    'small_stmt'
    'expr_stmt'
    'testlist'
    'test'
    'and_test'
    'not_test'
    'comparison'
    'expr'
    'xor_expr'
    'and_expr'
    'shift_expr'
    'arith_expr'
    'term'
    'factor'
    'power'
    'atom'
    NAME 'instance'
    'trailer'
    DOT '.'
    NAME 'method'
    'trailer'
    LPAR '('
    'arglist'
    STAR '*'
    'test'
    'and_test'
    'not_test'
    'comparison'
    'expr'
    'xor_expr'
    'and_expr'
    'shift_expr'
    'arith_expr'
    'term'
    'factor'
    'power'
    'atom'
    NAME 'args'
    RPAR ')'
    NEWLINE ''
    ENDMARKER ''

    What about other expressions that can retrieve a callable whose arguments you might want to compute
    with logic? My bet is that neither of us can predict what pain an arbitrary restriction on natural
    consequences of a good orthogonal grammar change will do. In another post I did sketch out a grammar
    mod that prevents a whole bunch of nonsense such as continue where ... (and I'm not sure I caught all
    true silliness), but I would rather not restrict for pure style reasons. Obviously the BDFL went the
    style-restrictive way with decorator expressions, since functionally any expression after the '@'
    which returned a callable taking a function as its first argument could have been allowed.
    I agree (after some mixed feelings ;-) about that in the decorator case, but I think where-functionality
    is much more general.


    >
    >- mark the func-call-scope by some keyword either "with" or "where"
    >because
    > it has to be separated from the calling environment both for
    >consistency
    > and user friendlyness

    Not sure what you mean by func-call scope, but I assume you are referring to
    what is created by processing the where suite.

    The way I have been thinking of this for where is to translate

    <tiny_stmt> where <wheresuite>

    to
    def uniquetmp(): # return dict with bindings created in wheresuite
    <where-suite>
    return vars()

    exec_with_shadowing_name_space( uniquetmp(), <tiny_stmt> )

    IOW, the idea is to execute the tiny_stmt normally, except that any names accessed
    will first be looked up in the dict returned by uniquetmp(), and if not found there
    will go on to the normal local space of tiny_stmt and then to its globals.

    Thunks are tricky, because while bound in the where suite namespace, their code bodies
    share the namespace outside of the where scope.

    I think I can implement the functionality with a combination of import-time AST rewriting
    and byte code hacks, but I am not sure I want to expend the energy doing that. Better to
    expend it getting set up to do real experimental changes. But I need to upgrade a bunch
    of stuff to do that ;-/

    >
    >- Simple matching rules for both keyword-parameters and
    >argument-tuples.

    This almost suggests source-level macro processing to me, which is _NOT_ what
    where is about. "Matching" in my view is not textual, it happens dynamically at run time
    and names not supplied will cause run time name errors, not syntax errors.

    > Make explicit which kind of inference rule is selected.

    I'm not sure what this means ;-)

    >
    >I think that my last proposal I finally distilled from our discussion
    >would pretty much fullfill all three requirements whereas that of
    >Andrey Tatarinov would be somewhat more restrictive and fullfills only
    >the first two.

    Maybe you could summarize it as a concrete grammar change? That would help
    make it clear. I enjoy this ping pong, but it takes up a lot of time ;-)

    Regards,
    Bengt Richter
     
    Bengt Richter, Apr 19, 2005
    #11
  12. Robert Brewer

    Ron Guest

    Kay Schluehr wrote:
    > Steven Bethard wrote:
    >
    >
    >>So the object of a "where" is then always an ordered dict?

    >
    >
    > Yes.
    >
    >
    >>If so, then
    >>I guess I like this proposal best so far.
    >>
    >>However, it does seem to have the problem that you can't have any
    >>additional local variables so, for example, list comprehensions are
    >>probably not usable...
    >>
    >>Or can you still drop the argument to "where" and just use the names
    >>directly? E.g.:
    >>
    >>x = property(fget=fget, doc=doc) where:
    >> doc = "I'm the 'x' property."
    >> def fget(self):
    >> return self.__x

    >
    >
    > I can't see why this shouldn't work?
    >
    > The specifiers behind "where" are present to determine the matching
    > behaviour. The order of a dict is caused by different specifiers i.e. a
    > dict- or tuple-like specifier. If a specifier is not present only names
    > can be matched regardless of a sequence and this is always possible
    > because we still have a dict with names as keys.
    >
    > What should not be possible are statements like this:
    >
    > x = property(a, b) where:
    > doc = "I'm the 'x' property."
    > def fget(self):
    > return self.__x
    >
    > because there is no rule to match doc and fget onto a and b. In this
    > case we would need specifiers:
    >
    > x = property(a, b)
    > where **a:
    > doc = "I'm the 'x' property."
    > where **b:
    > def fget(self):
    > return self.__x
    >
    > Ciao,
    > Kay



    Just throwing in another slight variation to Kay's example here.

    How about using ***name in the same way as *name, and **name are used?
    It extends the current argument options in a consistent manner and 'I
    believe' is easy to explain and visually says something different is
    happening here.

    This builds on the already present arg, *arg, **arg, and so why not a
    ***arg to represent the 4th alternative, a suite?

    You would also need to use it in the def statement as well, and probably
    would want to anyway if your argument suite is that long.

    def property(***a, ***b):
    a:
    doc = "I'm the default 'x' property."
    b:
    def foo(arg):
    return arg
    return arg


    x = property(***a, ***b)
    a:
    doc = "I'm the 'x' property."
    b:
    def fget(self):
    return self.__x


    With the ***name syntax, making it's obvious it's different (a good
    thing), and you might be able to drop the 'with' or 'where'.

    The (***name) == (name: suite) relationship may be useful in other
    places as well.

    I'm not sure on the returns for the def above, just trying to make it
    more complete. I presuming foo would be the default if 'b' isn't given
    in the function call. I suppose the ***b, and b: suite, in the function
    call can be optional. Same for the ***a and a: suite. By having them
    named, either one could be absent and the order could be swapped in the
    call if it makes it more readable.

    Cheers,
    Ron_Adam
     
    Ron, Apr 19, 2005
    #12
  13. Ron wrote:
    > How about using ***name in the same way as *name, and **name are used?
    > It extends the current argument options in a consistent manner and 'I
    > believe' is easy to explain and visually says something different is
    > happening here.
    >
    > This builds on the already present arg, *arg, **arg, and so why not a
    > ***arg to represent the 4th alternative, a suite?


    I dont' see how this is consistent. *arg and **arg correspond to the
    two different container protocols: sequence/iterable and mapping. Which
    container protocol do you se ***arg as corresponding to? Note that
    namespaces are just mappings, so if you want to treat a block like a
    namespace, **arg makes more sense...

    STeVe
     
    Steven Bethard, Apr 20, 2005
    #13
  14. Robert Brewer

    Ron Guest

    Steven Bethard wrote:

    > Ron wrote:
    >
    >> How about using ***name in the same way as *name, and **name are used?
    >> It extends the current argument options in a consistent manner and 'I
    >> believe' is easy to explain and visually says something different is
    >> happening here.
    >>
    >> This builds on the already present arg, *arg, **arg, and so why not a
    >> ***arg to represent the 4th alternative, a suite?

    >
    >
    > I dont' see how this is consistent. *arg and **arg correspond to the
    > two different container protocols: sequence/iterable and mapping. Which
    > container protocol do you se ***arg as corresponding to? Note that
    > namespaces are just mappings, so if you want to treat a block like a
    > namespace, **arg makes more sense...
    >
    > STeVe


    Another possibility is to use an additional symbol to indicate "not
    here", "over there" or "outside this": %**args. That has the advantage
    of allowing further expansion in the future if it's needed without
    getting into long strings of '******'s. Although I don't think that
    would happen.

    Too many various thoughts on this are are jumping in my mind right now.
    Which is partly why I've been staying out of this discussion. So here
    are some of them in no particular order.

    (Q1.) Is it possible that a new container might be added to python 3000
    (or earlier) to correspond to a names suite? Or a named lazy suite?
    ie.. one that might be evaluated at the function call versus at the
    define? Or evaluated upon request?

    [1] If so, the ***arg could be consistent in being a container protocol.


    (Q2.) Are the use of '*'s and '**'s allowed anywhere else beside within
    function call def's and call's? I presume not because it would conflict
    with multiply and exponent operators. So this suggests to me it is
    specific function 'use' case's of function arguments and not general
    container protocol representation that can be used else where.

    [1] If there are conflicts outside of function call use, then again
    **args should be preferred as not to create any additional conflicts.

    [3] If the use of '*', and '**', are used specifically to tell function
    calls and defs how to evaluate and use an argument vs a general
    container indicator. Then ***arg has the advantage of indicating to use
    a named suite or an dict to be defined within the function body. Named
    suites outside the function could use **arg as they would have already
    been evaluated.


    # arg suite defined in the function.
    x = function(***arg)
    arg:
    a = 1
    b = 22
    #continued ...
    #body


    # arg suite defined prior to function call.
    arg:
    a = 1
    #continued ...
    x = function(**arg)


    Q3. Which is more important, compiler/byte code representation of
    objects and operators or the user/syntax representation of objects and
    operators? Are we more concerned with the low level representation to
    the compiler or the higher level representation to the user?

    [1.] From a 'higher level'/'differing syntax' stand point, the ***arg
    alternative more clearly communicates that there is a difference in it's
    use to the programmer. (although the low level representation may be the
    same as **arg)

    [2.] If it's more important that an accurate representation of the lower
    level inner workings be maintained, then **args should again be preferred.


    As far as my reference to consistency, forget I said that. I think
    practicality might be more important than consistency here. Consistency
    between small groups of one to three items is not a very strong
    association in any case.


    Cheers,
    Ron_Adam
     
    Ron, Apr 20, 2005
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tim Hochberg
    Replies:
    0
    Views:
    275
    Tim Hochberg
    Aug 10, 2004
  2. Brian Sabbey

    pre-PEP: Suite-Based Keywords

    Brian Sabbey, Apr 16, 2005, in forum: Python
    Replies:
    31
    Views:
    742
    Bengt Richter
    Apr 19, 2005
  3. Robert Brewer

    RE: pre-PEP: Suite-Based Keywords

    Robert Brewer, Apr 18, 2005, in forum: Python
    Replies:
    2
    Views:
    272
    Bengt Richter
    Apr 19, 2005
  4. Micah Elliott

    Pre-PEP Proposal: Codetags

    Micah Elliott, Aug 11, 2005, in forum: Python
    Replies:
    8
    Views:
    302
    =?ISO-8859-1?Q?=22Martin_v=2E_L=F6wis=22?=
    Aug 12, 2005
  5. Daniel Berger
    Replies:
    11
    Views:
    265
    Chad Fowler
    May 12, 2005
Loading...

Share This Page