pre-PEP: Suite-Based Keywords

B

Bengt Richter

]
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>))
Update, sorry. Galloping evolution here ;-)

The '::' unary suite operator should return an ordered dict subtype representing the bindings, so

print items => {'x':1, 'y':[1, 2], 'foo':<function foo at 0x02EE8D14>}

instead. This allows cleaner suite-based keyword calling, since no dict:):<suite>) is necessary,

so
def foo(**kw): print kw

followed by

foo(**::
x = 1
y = [1,2]
def foo():pass)

will print the same. Since ::<suite> is an expression, it can go anywhere an expression can go.
I like orthogonality ;-)

Methods allow (with order preserved from binding in suite)

:): x=1; y=2).keys() # => ['x', 'y']
and
:): x=1; y=2).values() # => [1, 2]
and
:): x=1; y=2).items() # => [('x', 1), ('y', 2)]

note that :: is ;-greedy in one-line suites, so
:: x=1; y=2
is not
:): x=1); y=2

Regards,
Bengt Richter
 
K

Kay Schluehr

Bengt said:
]
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

I like this idea of "folding definitions into dicts" very much and
think that it is on the ground of suite-basing. It breaks down the
barrier between expressions and statements in Python without
annihilating it. I think it is also wise to restrict '::' to certain
accumulations. I don't like the idea of storing and dropping whole code
fragments in an irresponsible manner as Brian did for trapping the
compiler in his generator-to-function example of the thunks pre-PEP.

I think that '::' and 'where' are true and explicit representations of
the content of this proposal and be less redundant than the "as
<specifier>" syntax with it's various specifiers. Introducing '::'
would make the "folding definitions into dict" operation visible that
takes place in the machinery of the VM. Very Pythonic IMHO :)

+1 from me for '::' and 'where'.

Regards,
Kay
 
O

Oren Tirosh

Take a look at Nick Coglan's "with" proposal:

http://groups.google.co.uk/[email protected]

It addresses many of the same issues (e.g. easy definition of
properties). It is more general, though: while your proposal only
applies to keyword arguments in a function call this one can be used
to name any part of a complex expression and define it in a suite.

I think that a good hybrid solution would be to combine the "with"
block with optional use of the ellipsis to mean "all names defined in
the with block".

See also the thread resulting from Andrey Tatarinov's original
proposal (using the keyword "where"):

http://groups.google.co.uk/[email protected]


Oren
 
B

Bengt Richter

Take a look at Nick Coglan's "with" proposal:

http://groups.google.co.uk/[email protected]

It addresses many of the same issues (e.g. easy definition of
properties). It is more general, though: while your proposal only
applies to keyword arguments in a function call this one can be used
to name any part of a complex expression and define it in a suite.

I think that a good hybrid solution would be to combine the "with"
block with optional use of the ellipsis to mean "all names defined in
the with block".

See also the thread resulting from Andrey Tatarinov's original
proposal (using the keyword "where"):

http://groups.google.co.uk/[email protected]
Apologies for lack of attibution, Andrey. And I'm not sure I used your "where" properly just recently.
I added it to some other stuff in haste, without actually remembering exactly where it came from,
believe it or not, even though I got heavily into it in that thread. Somewhat disturbing memory lapse ;-/
I need some sleep ;-/

Regards,
Bengt Richter
 
R

Reinhold Birkenfeld

Bengt said:
Stretching for it, using my latest and greatest ;-)

y = f(**::
x = 1
y = 'y for f'
)*g(**::
x = 'x for g'
y = 'y for g'
def foo(): return 'foo for g'
)

Note that there is no problem adding other parameters, because ::<suite> is just
a unary expression returning dict subtype instance, e.g.,

y = f(11,22,**::
x = 1
y = 'y for f'
)*g(*args_from_somewhere, **::
x = 'x for g'
y = 'y for g'
def foo(): return 'foo for g'
)

You know that this is dead ugly?

The real ``problem'' (if you see one) is that the indentation syntax
doesn't allow for suites in expressions.

Reinhold
 
B

Brian Sabbey

Oren said:
Take a look at Nick Coglan's "with" proposal:

http://groups.google.co.uk/[email protected]

It addresses many of the same issues (e.g. easy definition of
properties). It is more general, though: while your proposal only
applies to keyword arguments in a function call this one can be used
to name any part of a complex expression and define it in a suite.

I think that a good hybrid solution would be to combine the "with"
block with optional use of the ellipsis to mean "all names defined in
the with block".

See also the thread resulting from Andrey Tatarinov's original
proposal (using the keyword "where"):

http://groups.google.co.uk/[email protected]

Thanks for the links. I wasn't aware of that proposal.

I agree that a hybrid solution is probably the way to go.

Does anyone know if the 'where' keyword is only for readability (or does
it disambiguate the syntax in some situations)? I think I prefer leaving
it off.

Maybe using '**' would be better than '...' since it already is used to
indicate keyword arguments. Example:

class C(object):
x = property(**):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x


-Brian
 
B

Bengt Richter

You know that this is dead ugly?
What aspect in particular? I.e., does this (currently legal) look prettier:

y = f(11,22, **dict(
x = 1,
y = 'y for f'
))*g(*args_from_somewhere, **dict(
x = 'x for g',
y = 'y for g',
foo = lambda: return 'foo for g'
))

Can you express the same semantics in a prettier way?

To boil it down, doesn't a suite bindings expression like

d = ::
x = 1
y = 'y for f'

(which in this case doesn't even need parens) seem prettier than

d = dict(
x = 1,
y = 'y for f'
)

to you, especially given that :): ...) gives you the power
of full suite syntax to create bindings
any way you want[1], not just keyword=<expression> ?
(and you can leave out the commas ;-)

[1] I.e., this should work to extend the power of the type expression in a way
that shows what you can't do with dict(...) ;-)

type('C', (), ::
def __repr__(self):
return '<alternatively-created-class-object at %08x>'% (hex(id(self)&(2L**32-1))
def cname(self): return type(self).__name__
classvar = 123
# ... anything you can do in a class definition body
)

IMO that's pretty clean.
The real ``problem'' (if you see one) is that the indentation syntax
doesn't allow for suites in expressions.

I was trying to solve that "problem" with my "suite expressions" ;-)

::<suite> # suite bindings expression (as ordered dict)
def(<arglist>):<suite> # anonymous def
(<arglist>):<suite> # thunk (anonymous callable suite sharing local namespace)

I think suite indentation rules for suite expressions are not that hard, once you decide
to deal with it as a separate indentation space from outside the expression. That's already
done to allow multiline expressions without indentation interpretation inside bracketed expressions.
This is just adding indentation processing within certain types of expressions ("suite expressions" ;-)

For the most part I like indentation syntax very well, and
I suspect that if there were optional brackets, you would still be indenting
for clarity, so the chances are the bracket version of the above would
mainly add bracket noise to something close to the above.

Regards,
Bengt Richter
 
B

Brian Sabbey

Brian said:
Does anyone know if the 'where' keyword is only for readability (or does it
disambiguate the syntax in some situations)? I think I prefer leaving it
off.

To answer my own question, I see by reading the where threads that using
the 'where' keyword allows things such as:

# Design by contract (from Nick Coghlan)
@dbc(pre, post)
def foo():
pass
with:
def pre():
pass
def post():
pass
 
S

Shane Hathaway

Brian said:
Maybe using '**' would be better than '...' since it already is used to
indicate keyword arguments. Example:

class C(object):
x = property(**):
doc = "I'm the 'x' property."
def fget(self):
return self.__x
def fset(self, value):
self.__x = value
def fdel(self):
del self.__x

Looks good to me. You should update the pre-PEP and submit it again to
the list.

This syntax might also be a good replacement for event handling when
lambda goes away (as Guido announced in his PyCon 2005 keynote.)

button = Button(self, "Push me")
button.addHandlers(**):
def onmouseover(event):
self.textBox.setText("Are you going to push me?")
def onmouseout(event):
self.textBox.setText("Too shy. Goodbye.")
def onclick(event):
self.textBox.setText("Ouch!")
oncontextmenu = self.oncontextmenu

Shane
 
R

Reinhold Birkenfeld

Bengt said:
What aspect in particular?

The '**::', for example. I would surely prefer a keyword instead of '::'.
I.e., does this (currently legal) look prettier:

y = f(11,22, **dict(
x = 1,
y = 'y for f'
))*g(*args_from_somewhere, **dict(
x = 'x for g',
y = 'y for g',
foo = lambda: return 'foo for g'
))

No, it doesn't. And I surely wouldn't write such code.

y = (f(11, 22, x=1, y='y for f') *
g(*args_from_somewhere, x='x for g', y='y for g', foo=lambda: return 'foo for g'))

or, if you tolerate more lines,

y = (f(11, 22, x=1, y='y for f') *
g(*args_from_somewhere,
x='x for g', y='y for g',
foo=lambda: return 'foo for g'))

would be my current way to express this. But still, the less lines,
the less confusing it is.
Can you express the same semantics in a prettier way?

To boil it down, doesn't a suite bindings expression like

d = ::
x = 1
y = 'y for f'

(which in this case doesn't even need parens) seem prettier than

d = dict(
x = 1,
y = 'y for f'
)

to you, especially given that :): ...) gives you the power
of full suite syntax to create bindings
any way you want[1], not just keyword=<expression> ?
(and you can leave out the commas ;-)

I understand the general idea, but still I don't like the idea of "suite expressions".
My main concern is the following: After a suite expression, where does code
follow?

As in your example:

y = f(**::
x = 1 # indented one level, as indents can go any number of spaces
) # indented one level too, but differently (currently an IndentantionError)
[1] I.e., this should work to extend the power of the type expression in a way
that shows what you can't do with dict(...) ;-)

type('C', (), ::
def __repr__(self):
return '<alternatively-created-class-object at %08x>'% (hex(id(self)&(2L**32-1))
def cname(self): return type(self).__name__
classvar = 123
# ... anything you can do in a class definition body
)

IMO that's pretty clean.

Uses are neat, i concur.
I was trying to solve that "problem" with my "suite expressions" ;-)

::<suite> # suite bindings expression (as ordered dict)
def(<arglist>):<suite> # anonymous def
(<arglist>):<suite> # thunk (anonymous callable suite sharing local namespace)

I think suite indentation rules for suite expressions are not that hard, once you decide
to deal with it as a separate indentation space from outside the expression. That's already
done to allow multiline expressions without indentation interpretation inside bracketed expressions.
This is just adding indentation processing within certain types of expressions ("suite expressions" ;-)

For the most part I like indentation syntax very well, and
I suspect that if there were optional brackets, you would still be indenting
for clarity, so the chances are the bracket version of the above would
mainly add bracket noise to something close to the above.

I don't say brackets are better, but the problem of where to put the following code
is tricky.

Reinhold
 
R

Ron_Adam

y = (f(11, 22, x=1, y='y for f') *
g(*args_from_somewhere,
x='x for g', y='y for g',
foo=lambda: return 'foo for g'))

would be my current way to express this. But still, the less lines,
the less confusing it is.

I would probably do it this way.

y = f(11, 22, x=1, y='y for f') \
* g( *args_from_somewhere,
x='x for g',
y='y for g',
foo=lambda: return 'foo for g' )

I tend to put the opperators on the left for continued lines. It's
nice visual que to whats happening.

if (a==1
or b==2
or c==3):
x = ( 1.333333333333
+ the_last_value_i_needed
+ the_first_value_i_started_with
+ another_long_name_for_something )

This subject really hasn't been a problem for me. So I really don't
see the point of adding a new syntax.

And this works on the def side.

def f( first,
second,
x=0,
y='' ):
#
# rest of body
#

So is this new syntax just a way to keep the '()'s closer together?

Cheers,
Ron
 
B

Bengt Richter

The '**::', for example. I would surely prefer a keyword instead of '::'.

I thought it was concise and mnemonic, in that ':' already introduces suites, and '::'
introduces exactly the same thing, only processing it differently, which is suggested
concisely by '::' being different from ':' ;-)
No, it doesn't. And I surely wouldn't write such code.

y = (f(11, 22, x=1, y='y for f') *
g(*args_from_somewhere, x='x for g', y='y for g', foo=lambda: return 'foo for g'))

or, if you tolerate more lines,

y = (f(11, 22, x=1, y='y for f') *
g(*args_from_somewhere,
x='x for g', y='y for g',
foo=lambda: return 'foo for g'))

would be my current way to express this. But still, the less lines,
the less confusing it is.
Chacun a son gout ;-)
Can you express the same semantics in a prettier way?

To boil it down, doesn't a suite bindings expression like

d = ::
x = 1
y = 'y for f'

(which in this case doesn't even need parens) seem prettier than

d = dict(
x = 1,
y = 'y for f'
)

to you, especially given that :): ...) gives you the power
of full suite syntax to create bindings
any way you want[1], not just keyword=<expression> ?
(and you can leave out the commas ;-)

I understand the general idea, but still I don't like the idea of "suite expressions".
My main concern is the following: After a suite expression, where does code
follow?

As in your example:

y = f(**::
x = 1 # indented one level, as indents can go any number of spaces
) # indented one level too, but differently (currently an IndentantionError)
I don't understand your example. Think of it as a triple quoted string, except that it opens
with :: and doesn't require \ to ignore the first blank line, and it closes with dedent or
a closing bracket that was not opened in the "string".
That closing paren doesn't belong to the :: suite (having been opened outside the suite).
Therefore, no matter where it appears, it will end the suite, and play its own role in
matching its partner (right after f here).

If you parenthesize you are fully free to indent your first line anywhere, e.g. (perversely)
y = f(**:): # note paren
x = 1
) # blank line with 7 spaces ends the suite
# you can't get left of the reference indentation here, so it's
# good that ')' ends the suite ;-)
) # closes f( -- legal at any indentation, since we're inside an open bracket

If you don't parenthesize, your suite will end where a normal if x: suite would end by dedent
Note that
if x:
z=2
is a syntax error, and you would expect the same with
x = ::
z = 2
if :: required something on the next line, but unlike an if with no inline suite, :: doesn't require it,
and just sees that z is at or left of the existing indentation of x, so the z is a dedent ending a blank suite,
and z better line up with something, as in

if x:
x = :: # blank suite
z = 2

So you can write
d1 = ::
x = 1
d2 = :: x=2
d2 = ::
def foo(): pass

and expect what you'd expect if you were me ;-)


BTW, surrounding brackets such as f(...) do not do away with the indentation rules
_within a_ :: suite, they just let you start it where you want.
[1] I.e., this should work to extend the power of the type expression in a way
that shows what you can't do with dict(...) ;-)

type('C', (), ::
def __repr__(self):
return '<alternatively-created-class-object at %08x>'% (hex(id(self)&(2L**32-1))
def cname(self): return type(self).__name__
classvar = 123
# ... anything you can do in a class definition body
)

IMO that's pretty clean.

Uses are neat, i concur.
Maybe I can win you over ;-)
I don't say brackets are better, but the problem of where to put the following code
is tricky.
Why is it more tricky than the suite of if x: ?
And when can't you easily resolve a doubt with explicit parens?

Regards,
Bengt Richter
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top