remaining decorator syntax options

S

Steven Bethard

So here's the state of the decorator debate as I see it:


*** Location

GvR pretty strongly wants decorators before the function:

http://mail.python.org/pipermail/python-dev/2004-August/047112.html
http://mail.python.org/pipermail/python-dev/2004-August/047279.html

The idea being that decorators and other function properties should
not look like part of the function's code. I don't think there's much
chance of swaying GvR on this count.


*** Indentation

Since location has been pretty much decided, I see three options here:

(1)
<indicator> dec
def f():
pass

(2)
<indicator>
dec
def f():
pass

(3)
<indicator> dec
def f():
pass

GvR definitely disliked the third version because it meant that class
functions could all be at different indentation levels. The PEP
suggests that the second version was disliked because it suggests that
"using" is introducing a new scope (although arguments about if/else
behavior seemed to counter this somewhat). There didn't seem to be
any arguments that made version 2 drastically better than version 1
though, so it seems most likely that a BDFL pronouncement will give us
version 1.


*** List notation

Assuming a pre-def, non-indented decorator, here are the remaining
list notations:

(1)
<indicator> dec1
<indicator> dec2
<indicator> dec3

(2)
<indicator> [dec1, dec2, dec3]

(3)
<indicator> dec1, dec2, dec3

I believe that version 3 was vetoed because it makes it too hard to
break lists of decorators across lines. GvR's preference is
definitely for 1, but that may be influenced by his decision for the
indicator. I have a feeling that how long the indicator is directly
affects which of (1) or (2) is preferred. Compare:

@dec1
@dec2
@dec3

vs.

@[dec1, dec2, dec3]

and

using dec1
using dec2
using dec3

vs.

using [dec1, dec2, dec3]

It seems like with a single symbol character, the non-list syntax is
much more concise, and probably clearer. However with a keyword
syntax the list seems the better option.

Which brings us, of course to the final section:

*** Indicator

The options seemed to be:

(1) a symbol (@, |, etc.)
(2) a keyword (using, decorate, etc.)
(3) similar to function
(4) no indicator

Options (3) and (4) were ruled out because (given that we're already
relegated to a before-def syntax) they are already valid syntax, and
redefining them for decorators means changing the expected semantics
of the statements.

Option (1), with @, is GvR's favorite, and does have something of a
Java backing. It has the substantial disadvantage that there is no
particular reason (or even some sort of mnemonic) for which @ (or |
for that matter) should mean "decorator".

With option (2), we can choose a keyword that does suggest a
decorator. Even "using", which is not an outstanding choice, is at
least more intuitive than '@'. Option (2) does have the disadvantage
that it requires introducing a new keyword, but this is not
insurmountable -- it was done before for generators. And GvR even
said that he would consider this option if there enough support for
it.

----

So here's my suggestions:

Let's leave the location and indentation arguments behind. We're not
making much progress on them, and I don't think there's much chance of
swaying GvR. Reiterating all the old arguments won't get us closer to
a syntax we're happy with.

I think once the indicator argument is decided, the list-structure
decision will fall out of it.

So I'd suggest that we put all our effort into the indicator
discussions. If you have a good argument for why a keyword is better
than a symbol, or why a symbol is better than a keyword, please voice
them now. And remember that 'because it's prettier' or 'because it's
uglier' are not aguments GvR will consider. We need strong arguments,
that argue on the basis of readability or learnability or
maintainability, etc.


.... and my opinion:

A keyword is much more Pythonic than a symbol:

A well chosen keyword can suggest the meaning of the construction,
e.g. "using ... def ..." suggests that the decorators are used as part
of the definition of the function. A symbol like '@' or '|' cannot
easily suggest the meaning in this manner.

For this reason, a keyword makes both reading and learning Python
decorator syntax easier. If you encounter "using ... def ..." and
have to guess at it's meaning, your knowledge of English "using" can
now be applied to guess the meaning of Python "using". This is not
true of a symbol like '@' or '|'.

(An additional argument might be that it will be at least /possible/
to Google "using", but impossible to do so for "@" or "|". This was
one of the frustrating things about Perl for me -- if I didn't know
what a symbol did, I couldn't Google it because it wasn't a word.
Remember that if you don't know it's a decorator, you don't know to
search for "decorate" ;)


hoping-to-keep-this-dicussion-productive-ly-yrs,

Steve
 
G

Grzegorz Dostatni

Keyword. Definitely keyword.

One of the great stong points of python (for me at least) is that it uses
next to no special symbols. Now, I could go on and say how ugly I find
code that uses these kinds of symbols. I've been doing python programming
for about 5 years. I've done ruby for the last two years and I still find
those @, @@ and $ to be both ugly and annoying to write. Whenever I am
forced to include a special symbol, I pretty much stop and rever to
"hunting and pecking".

By comparison, writing python code is not much more difficult than writing
english text. (Although it requires far more concentration). Besides,
typing speed is not an issue in programming. Saving 4 keystrokes by going
@foo instead of self.foo is not going to make a difference. What makes
python a rapid development language is documentation, powerful base data
types and the ability to read other people's code. Most of the time ;-)

At the end of the day, I would look for precedents:

Python: c/c++ Ruby
and & and
or | or
self.foo @foo
global $foo

I am aware of the symbols python uses (#, %, **, &, |). With the exception
of #, I generally find a way to avoid them.

Greg
 
S

Shalabh Chaturvedi

Steven Bethard wrote:
....
*** List notation

Assuming a pre-def, non-indented decorator, here are the remaining
list notations:

(1)
<indicator> dec1
<indicator> dec2
<indicator> dec3

(2)
<indicator> [dec1, dec2, dec3]

(3)
<indicator> dec1, dec2, dec3

I believe that version 3 was vetoed because it makes it too hard to
break lists of decorators across lines. GvR's preference is
definitely for 1, but that may be influenced by his decision for the
indicator. I have a feeling that how long the indicator is directly
affects which of (1) or (2) is preferred. Compare:

@dec1
@dec2
@dec3

vs.

@[dec1, dec2, dec3]

and

using dec1
using dec2
using dec3

vs.

using [dec1, dec2, dec3]

It seems like with a single symbol character, the non-list syntax is
much more concise, and probably clearer. However with a keyword
syntax the list seems the better option.

For short single names, yes. But also compare

@staticmethod
@foo.moredecoration(a="hello",b="world")
@foo.bar.baz()
def myfunc():

vs.

using [staticmethod ,foo.moredecoration(a="hello",b="world"), foo.bar.baz()]
def myfunc():

(I find the first version clearer.)

In some usability testing I did myself, I found that having one
decorator per line increased usability because:

1. All decorators line up at the left directly above the function name.
When you look at a function it's immediately visible how it is decorated
- no scanning required.

2. Moving and deleting decorators around is easy (cutting-pasting entire
lines is easier than parts of lines).

3. Adding decorators is easy (you just have to locate the start of a
line, not somewhere in the middle).

So I would not support multiple decorators per line.

Btw, I think your summary of the current state is good. However I feel a
new keyword has been pretty much ruled out as well.

Since there are a number of supporters of GvR's pie-decorator syntax
(myself being one of them), I doubt we will see anything else unless
most of us @-supporters are swayed. So I am happy, unless we end up with
the flagstaff syntax:

|staticmethod
|foo.moredecoration(a="hello",b="world")
|foo.bar.baz()
def myfunc():
pass

In which case I am happier :)

Cheers,
Shalabh
 
P

Peter Hansen

Grzegorz said:
At the end of the day, I would look for precedents:

Python: c/c++ Ruby
and & and
or | or
self.foo @foo
global $foo

I am aware of the symbols python uses (#, %, **, &, |). With the
exception of #, I generally find a way to avoid them.

Corrections to the above:

C/C++ use && and || for "and" and "or". The single-char versions are
operators used in mathematical expressions, and used identically
in Python (and Ruby? or does it use words for both?).

As for the "symbols" of Python: % ** & and | are all math
operators, so they don't really apply here in the same way
as other things do. Avoiding them would be senseless, given
that when you need them, you need them, in the same way you
wouldn't go around trying to avoid + or / either.

# to introduce a comment is clearly in line with other languages
that use symbols instead of words ("rem" from BASIC? Ugh...) but
it is also a _very_ widely used convention at this point, and
comes directly from Python's ancestry in system admin stuff.

In the end, though, a new keyword would pretty much rock for this
whole mess, especially if it were to immediate follow the function
def's colon, and have it's own colon with an indented block following.

-Peter
 
B

Bengt Richter

So here's the state of the decorator debate as I see it:


*** Location

GvR pretty strongly wants decorators before the function:

http://mail.python.org/pipermail/python-dev/2004-August/047112.html
http://mail.python.org/pipermail/python-dev/2004-August/047279.html

The idea being that decorators and other function properties should
not look like part of the function's code. I don't think there's much
chance of swaying GvR on this count.


*** Indentation

Since location has been pretty much decided, I see three options here:

(1)
<indicator> dec
def f():
pass

(2)
<indicator>
dec
def f():
pass

(3)
<indicator> dec
def f():
pass
(4)
dec<indicator>
def f():
pass

Suggested indicator: '(=' or ':)' or '(%' e.g.,

dec(=
def f():
pass
GvR definitely disliked the third version because it meant that class
functions could all be at different indentation levels. The PEP
suggests that the second version was disliked because it suggests that
"using" is introducing a new scope (although arguments about if/else
behavior seemed to counter this somewhat). There didn't seem to be
any arguments that made version 2 drastically better than version 1
though, so it seems most likely that a BDFL pronouncement will give us
version 1.


*** List notation

Assuming a pre-def, non-indented decorator, here are the remaining
list notations:

(1)
<indicator> dec1
<indicator> dec2
<indicator> dec3

(2)
<indicator> [dec1, dec2, dec3]

(3)
<indicator> dec1, dec2, dec3
(4)
dec1<indicator>
dec2<indicator>
dec3<indicator>

e.g.,

dec1(=
dec2(=
dec3(=
def f(): pass

I believe that version 3 was vetoed because it makes it too hard to
break lists of decorators across lines. GvR's preference is
definitely for 1, but that may be influenced by his decision for the
indicator. I have a feeling that how long the indicator is directly
affects which of (1) or (2) is preferred. Compare:

@dec1
@dec2
@dec3

vs.

@[dec1, dec2, dec3]

and

using dec1
using dec2
using dec3

vs.

using [dec1, dec2, dec3]

It seems like with a single symbol character, the non-list syntax is
much more concise, and probably clearer. However with a keyword
syntax the list seems the better option.

Which brings us, of course to the final section:

*** Indicator

The options seemed to be:

(1) a symbol (@, |, etc.)
(2) a keyword (using, decorate, etc.)
(3) similar to function
(4) no indicator
(5) postfixed indicators [ :) (= (% <<< etc ]
dec1 (=
dec2 (=
def foo(): pass

Options (3) and (4) were ruled out because (given that we're already
relegated to a before-def syntax) they are already valid syntax, and
redefining them for decorators means changing the expected semantics
of the statements.

Option (1), with @, is GvR's favorite, and does have something of a
Java backing. It has the substantial disadvantage that there is no
particular reason (or even some sort of mnemonic) for which @ (or |
for that matter) should mean "decorator".

With option (2), we can choose a keyword that does suggest a
decorator. Even "using", which is not an outstanding choice, is at
least more intuitive than '@'. Option (2) does have the disadvantage
that it requires introducing a new keyword, but this is not
insurmountable -- it was done before for generators. And GvR even
said that he would consider this option if there enough support for
it.

----

So here's my suggestions:

Let's leave the location and indentation arguments behind. We're not
making much progress on them, and I don't think there's much chance of
swaying GvR. Reiterating all the old arguments won't get us closer to
a syntax we're happy with.

I think once the indicator argument is decided, the list-structure
decision will fall out of it.

So I'd suggest that we put all our effort into the indicator
discussions. If you have a good argument for why a keyword is better
than a symbol, or why a symbol is better than a keyword, please voice
them now. And remember that 'because it's prettier' or 'because it's
uglier' are not aguments GvR will consider. We need strong arguments,
that argue on the basis of readability or learnability or
maintainability, etc.
You don't think GvR goes for prettier, other things being equal?
... and my opinion:

A keyword is much more Pythonic than a symbol:
Yes, except that what we have is really nested function calls,
passing a special argument, so maybe the decorating functions
ought to look like they are being invoked as functions. I.e.,

deco1(
deco2(
arg_that_happens_to_be_created_by_a_def_statement_and_suite
))

But, since it's not an ordinary argument list, we need an alternative
to the function-calling expression trailer (...) that yet suggests calling.
Since the def defines its own end, we don't need the )) above, but we do
need a different '(' -- so I'm suggesting (= or maybe :) or (% or <<<, so
the above simply becomes

deco1(=
deco2(=
arg_that_happens_to_be_created_by_a_def_statement_and_suite

and deco1 and deco2 can just be arbitrary expressions evaluating to something
callable.

If other statement:suite blocks can be interpreted as defining arguments useful
to pass to a function, then
func(=
statement: suite
could be used in a generalized way to operate on those. The loose end here is
re/binding the function (or class, etc) name. I.e., would you want to write

foo = deco1(=
deco2(=
def foo():
pass
?
Or maybe

foo = (
deco1(=
deco2(=
def foo():
pass
)

So long as postfixed (= is just sugar for the current use case,
it doesn't matter, of course. The rebinding can be a side effect
of an expression whose value is discarded.

A well chosen keyword can suggest the meaning of the construction,
e.g. "using ... def ..." suggests that the decorators are used as part
of the definition of the function. A symbol like '@' or '|' cannot
easily suggest the meaning in this manner.
But deco(...) suggests calling deco with something, and that's what's happening.
We just need to distinguish the def-result-as-argument variant, IMO.
For this reason, a keyword makes both reading and learning Python
decorator syntax easier. If you encounter "using ... def ..." and
have to guess at it's meaning, your knowledge of English "using" can
now be applied to guess the meaning of Python "using". This is not
true of a symbol like '@' or '|'. see above ;-)

(An additional argument might be that it will be at least /possible/
to Google "using", but impossible to do so for "@" or "|". This was
one of the frustrating things about Perl for me -- if I didn't know
what a symbol did, I couldn't Google it because it wasn't a word.
Remember that if you don't know it's a decorator, you don't know to
search for "decorate" ;)
I do like google. How about a postfixed keyword indicator like 'munges' ;-)

deco1 munges
deco2 munges
def foo(): pass

;-)
hoping-to-keep-this-dicussion-productive-ly-yrs,
hoping-to-have-added-something-useful-ly-yrs,

Regards,
Bengt Richter
 
J

John Carter

*** List notation

Assuming a pre-def, non-indented decorator, here are the remaining
list notations:

(1)
<indicator> dec1
<indicator> dec2
<indicator> dec3

(2)
<indicator> [dec1, dec2, dec3]

(3)
<indicator> dec1, dec2, dec3

I believe that version 3 was vetoed because it makes it too hard to
break lists of decorators across lines. GvR's preference is
definitely for 1, but that may be influenced by his decision for the
indicator. I have a feeling that how long the indicator is directly
affects which of (1) or (2) is preferred. Compare:

@dec1
@dec2
@dec3

vs.

@[dec1, dec2, dec3]

and

using dec1
using dec2
using dec3

vs.

using [dec1, dec2, dec3]
If one must have a new keyword, i.e. usin, then how about following
the syntax of the rest of python's control structures.

using:
dec1
dec2
dec3
def func():
pass

An alternative syntax I thought of, which dosnt use a new key word was

def func() with [dec1, dec2, dec3]:
pass

'with' has the same sort of association feeling as 'using'

This is probably ruled out of court as being after the fnction name.
It does have the merit of generating a syntax error in 2.3

John Carter
 
S

Steven Bethard

Shalabh Chaturvedi said:
@staticmethod
@foo.moredecoration(a="hello",b="world")
@foo.bar.baz()
def myfunc():

vs.

using [staticmethod ,foo.moredecoration(a="hello",b="world"), foo.bar.baz()]
def myfunc():

(I find the first version clearer.)

Yup. Though I think most people would write the second option
something like:

using [staticmethod,
foo.moredecoration(a="hello",b="world"),
foo.bar.baz()]
def myfunc():

(with, of course, a hundred different tabulation options there, but
you get the idea). My point was that with the keyword, you save a lot
of keystrokes using the list if you have a large number of decorators,
but with a single symbol, of course, using a list only adds
keystrokes. You make some good points though, and I would be just as
happy with:

using staticmethod
using foo.moredecoration(a="hello",b="world")
using foo.bar.baz()
def myfunc():
Btw, I think your summary of the current state is good. However I feel a
new keyword has been pretty much ruled out as well.

Can you refer me to some pages that made you feel this way? GvR said
that he "won't rule it out just because of the new keyword":

http://mail.python.org/pipermail/python-dev/2004-August/047001.html

I also haven't seen any arguments against keywords that said anything
but basically "introducing a new keyword might break existing code".
Not that this isn't a valid argument, but as Python has gone through a
similar process recently for 'yield', I'd feel more like keywords were
ruled out if I heard some other arguments.

Steve
 
S

Shalabh Chaturvedi

Steven said:
Can you refer me to some pages that made you feel this way? GvR said
that he "won't rule it out just because of the new keyword":

http://mail.python.org/pipermail/python-dev/2004-August/047001.html

I also haven't seen any arguments against keywords that said anything
but basically "introducing a new keyword might break existing code".
Not that this isn't a valid argument, but as Python has gone through a
similar process recently for 'yield', I'd feel more like keywords were
ruled out if I heard some other arguments.

Steve

You're right - it hasn't been entirely ruled out. I just got the feeling
reading python-dev that new keywords are shunned unless very convincing
reasons are found. Guido's words, from the post you mention: "this is a
softening of my position on new keywords". So I doubt a keyword will be
picked for decorators, but let's see.

Shalabh
 

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

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top