An Alternate Suite Delineation Syntax For Python? (was Re: [Python-Dev] [PATCH] Adding braces to __future__)

A more appropriate way to phrase this suggestion would be one that clearly states the problems with the status quo and explores them, rather than assuming an answer, and indulging in a long rambling diatribe against those that will shout you down for adding nothing new to the discussion. Step 1: post to the right mailing list (i.e. python-ideas, not python-dev) Step 2: don't assume the conclusion in the subject line. Ask a question, don't state an imperative. For example: "An Alternate Suite Delineation Syntax For Python?" Step 3: respect the time of others, by trimming your message to its core essence For example: An Alternate Suite Delineation Syntax For Python? ================================================= Python's whitespace based delineation of suites is one of its greatest strengths. It aligns what the human reader perceives with what the computer is actually executing, reducing the frequency of semantic errors due to mismatches between the use of separate block delimiters and the human readable indentation. However, this benefit comes at quite a high price: it is effectively impossible to embed arbitrary Python statements into any environment where leading whitespace is *not* significant, including Python's own expression syntax. It can be argued that this restriction has led directly to the introduction of "expression friendly" variants of several Python top level constructs (for example, lambda expressions, conditional expressions and as a contributing factor in creating the various forms of comprehension). It is also one of the reasons Python-based templating languages almost always create their own custom syntax - embedding Python's own whitespace sensitive statement syntax into environments where leading whitespace is either ignored or forms a significant part of the template output is a formidable challenge. In other languages, this kind of issue is handled by using explicit suite and statement delimiters (often braces and semi-colons, respectively) to allow full suites to be used as expressions. While Python uses braces for another purpose (dictionary and set definitions), it is already the case that semi-colons (';') can be used as statement terminators, both optionally at the end of any simple statement, and also to combine multiple simple statements into a single larger statement (e.g. "x += y; print(x)"). It seems that this existing feature could be combined with a brace-based notation to create an unambiguous "suite expression" syntax that would enjoy the same semantics as ordinary Python suites (i.e. doesn't create a new scope, doesn't directly affect control flow), but allows *all* Python statements to be embedded inside expressions. Currently, the character sequence "{:" is a Syntax Error: you are attempting to end a compound statement header line while an opening brace remains unmatched, or else trying to build a dictionary without specifying the key value. This creates an opportunity to re-use braces for a suite expression syntax without conflicting with their use for set and dictionary construction. Specifically, it should be possible to create a variant of the top-level Python syntax that: 1. Explicitly delimits suites using the notation "{:" to open the suite and "}" to end it 2. Requires the use of ";" to separate simple statements (i.e. newline characters would not end a statement, since we would be inside an expression) 3. Requires that all subordinate suites also be suite expressions (i.e. leading whitespace would not be significant, since we would be inside an expression) 4. Returns the value of the last statement in the suite as the result of the suite expression (since return statements would affect the containing scope) 5. Anonymous class and function definitions would be permitted in a suite expression (but accepting only a suite expression instead of an ordinary suite) (Ruby block notation and C's comma expressions are pretty much direct inspiration for the above list) Some examples: Raise expressions: x = y if y is not None else {: raise ValueError("y must not be None!")} Try expressions: x = {: try {: y.hello} except AttributeError {: "world!"}} With expressions: data = {: with open(fname) as f {: f.read()}} Embedded assignments: if {: m = pat.search(data); m is not None}: # do something with m else: # No match! In-order conditional expressions: {: if a {:b} else {:c}} One-liner accumulator function: def acc(n) {: s=n; return {: def (i) {: nonlocal s; s += i; return s}}} Cheers, Nick. P.S. I actually think the above idea is kinda cryptic and ugly. It is, however, extraordinarily powerful, especially when it comes to embedding Python code inside other environments that may not be friendly to significant leading whitespace. Applying the excellent set of criteria suggested by Mike Meyer: 1. What's the use case? Cleanly embedding arbitrary Python statements inside environments that are not generally sensitive to leading whitespace, such as templating languages, Python expressions and Python strings. 2. Does it make such code more readable? Yes, I believe it does, by providing "one obvious way to do it". Currently there are a mish-mash of alternatives out there (or else people give up and use something else entirely, like Javascript). 3. Does it encourage writing unreadable code? Perhaps. You certainly end up with a lot of braces, colons and semi-colons floating around. However, that's also something that can be addressed by style guides - if people are using the embedded syntax when the indented syntax would work fine, the problem isn't really due to the embedded syntax, it's due to people not caring about maintainability. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sat, 2011-12-10 at 15:06 +1000, Nick Coghlan wrote:
;-) In your example returning an unnamed function is a new feature in addition to the braces. It would probably need to be more like this... def acc(n){:s=n; def add(i){:nonlocal s; s+=i; return s};return add} There are a couple of issues to work out. Statements (def, class, for, while, and with) don't work after simi-colons. There may be others. Outside those issues, all you really need is an exit suit token. The colon can still be the open suit. Two semi-colons could do it. def acc(n):s=n; def add(i):nonlocal s; s+=i; return s;; return add The real issues are in getting the statements to work. Cheers, Ron

On 10 December 2011 05:06, Nick Coghlan <ncoghlan@gmail.com> wrote:
This, along with the original posting on python-dev, is a spectacular example of how to propose a new feature, and how not to. It would make an excellent example for a HOWTO document on proposing changes (with the OP's permission - he might not like his posting being held op as the canonical "how not to do this" example...) I barely read the original posting, even though I tried to follow it - my mind was screaming "ain't never going to happen" at me throughout. And yet Nick's rewording is pretty persuasive, I can see the benefits and the power of the idea. My only major objection is that the resulting code is pretty ugly. I could easily imagine something along these lines being accepted into Python. Thanks, Nick, for doing this - and Cedric for the original idea, even though I didn't appreciate it at the time :-) It certainly got me thinking. As regards the actual proposal, some comments: - It's got serious power (if, try and with expressions, etc etc) - It would answer a number of requests that keep coming up over and over again. - I suspect the semantics in corner cases will be hard to pin down, but the basic principle looks simple enough - The code is pretty ugly, and certainly an attractive nuisance for obfuscated code, but I can't think of a better syntax - Given that it effectively replaces lambdas and if expressions, it partly violates OOWTDI - and it does so in a way that could encourage people to still propose try or with expressions ("they are as common as conditionals, so why not?") Paul.

On 2011-12-10, at 12:32 , Paul Moore wrote:
An other possibility (with some precedent in generator comprehensions) would be to declare current statements to be expressions (and define their expressive semantics) and surround them with parens for disambiguation purposes when necessary.
On 2011-12-10, at 06:06 , Nick Coghlan wrote:

On Sat, 10 Dec 2011 15:06:13 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
Since you sent it to Python-ideas, I'm going to assume you were at least semi-serious. So let me add the use case I came up with while reading the original post, also often seen in template languages: Writing code to generate python. With significant whitespace, you have to keep explicit track of the current nesting level in order to generate the appropriate indent. With real delimiters, you can drop that. Compare: def generate(self): yield '{keyword} {expr}{:'.format(keyword=self.name, expr=self.expresion) for s in self.suite: for l in s.generate(): yield l With: def generate(self, level): yield '{0}{keyword} {expr}:'.format(' ' * level, keyword=self.name, expr=self.expresion) for s in self.suite: for l in s.generate(level + 1): yield l A minor improvement in just that one method, but it will show up in every generate method of every class, making it significant in total.
Applying the excellent set of criteria suggested by Mike Meyer:
"Suggested" may be a bit to much credit. I've observed the community applying these criteria to pretty much every idea. Some ideas get adopted in spite of failing one or more of them, but a *lot* of ideas get dropped after failing one of them. In this case: 1. What's the use case? Writing code that's going to write python code.
2. Does it make such code more readable?
It makes the code generating code less complicated, which means more readable.
I think not. As someone else observed, Haskell has both options available - and the rules for the indented version are a lot less straightforward than they are in Python (at least I haven't seen as clean a statement of them). About the only use of the delimited version in real code is turning a short suite into a one-line suite. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Mike Meyer wrote:
That's hardly an onerous requirement. As you show, the two code snippets are almost identical, except that the indent version takes an explicit level argument, while your braces version neglects to close suites at all. A fair comparison should show the close-suite code, even if it is only a single line.
I guess you would need to yield '}' after the outer for loop.
I wouldn't use the term "minor improvement" so much as "trivial difference". But even if I grant that it is an improvement, how many generate methods do you expect there to be? Presumably most of them will inherit their generate method from one or two parent classes. Even if every block keyword gets an independent copy in violation of DRY, there's still fewer than a dozen classes needed: def class for while if elif else try except finally with. So the total significance is still likely to be small. But wait... now you have your code generator, which generates ugly, unreadable code. Code needs to be read more often than written, even generated code, so now you need a code beautifier to reformat it using indentation for the human reader. Which means keeping track of the indent level. You're right back where you started, only worse, because now you have to parse the generated code to reformat it, instead of formatting it correctly the first time. So to save a dozen lines in your code generator, you end up with an extra hundred lines in a code beautifier. A classic example of code pessimation (of LOC rather than speed in this case). I think it is suggestive that we will need to warn people not to use this hypothetical syntax. As Nick wrote:
People who don't care about maintainability don't care about readability and they certainly don't read style guides unless they are forced to.
When you defend a feature on the basis that "people will hardly ever use it, except for trivial pieces of code that don't need it", that speaks volumes. -- Steven

On Sun, Dec 11, 2011 at 11:55 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Indeed, that's a reason I didn't use straight up code generation as my motivating use case - the significant whitespace simply isn't that painful in that case. The reason significant leading whitespace can be a PITA is due to two main circumstances: 1. Attempting to transport it through a channel that either strips leading and trailing whitespace from lines, or else consolidates certain whitespace sequences into a single whitespace character (generally sequences of spaces and tabs becoming a single space). Python source code simply cannot be passed through such channels correctly - if they don't offer an escaping mechanism, or that mechanism is not applied correctly, the code *will* be corrupted. Explicitly delimited code, on the other hand, can be passed through without semantic alteration (even if the details of the whitespace change) and a pretty printer can fix it at the far end. 2. Attempting to transport it through a channel where leading whitespace already has another meaning. This comes up primarily with templating languages - your whitespace is generally part of the template output, so it becomes problematic to break up your Python code across multiple code snippets while keeping the suite groupings clear. With explicit delimiters, though, you can just ignore the template parts, and pretend the code snippets are all part of a single string (the following is based on an example a colleague sent me [1], with the closing delimiter adjusted to mirror the opening one as a couple of folks suggested): <% if danger_level > 3 {: %> <div class="alert"><% if danger_level == 5 {: %>EXTREME <% :} %>DANGER ALERT!</div> <% elif danger_level > 0 {: %> <div>Some chance of danger</div> <% else {: %> <div>No danger</div> <% :} %> <% for a in ['cat', 'dog', 'rabbit'] {: %> <h2><%= a %></h2> <p><%= describe_animal(a) %></p> <% :} %> [1] https://gist.github.com/1455210 Is such code as beautiful and readable as normal Python code? No, of course not. But it would serve a purpose in defining a *standard* way to embed Python code in environments where the leading whitespace causes problems. In terms of how such a delimited syntax would relate to currently legal Python code, the transformation is basically the reverse of the Haskell one: 1. If you encounter a ":" to open a new suite, replace it and any trailing whitespace with "{:". 2. All whitespace between statements in such a suite is replaced with ";" characters 3. Any trailing whitespace after the last statement in the suite is replaced with ":};" The rest of the changes (i.e. a suite's expression value being the result of the last statement executed, anonymous function and class definitions) then follow from the fact that you now have a suite syntax that can potentially be embedded as an expression, so a range of new possibilities open up. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sun, 11 Dec 2011 09:44:17 +0100 Masklinn <masklinn@masklinn.net> wrote:
Users generally aren't expected to read generated code. It's pretty ugly for systems that generate python now. The reason you generate python instead of pyc is that backward compatibility of python source is a priority for python development, so with a little care your generated code will work across multiple versions of python. The same is not true for byte code files (and if that's changed with 3.x, I couldn't find anything saying so), so your code generator is liable to depend on the version of the target python interpreter. On Sun, 11 Dec 2011 12:55:51 +1100 Steven D'Aprano <steve@pearwood.info> wrote:
Correct. I thought it, but forgot to add it.
I wouldn't refer to any change that involves adding a variable as "trivial". Especially one that's part of the API. Part of the issue is that I understated the problem - this change is ubiquitous, and large enough that it adds up.
But even if I grant that it is an improvement, how many generate methods do you expect there to be?
Because it's in the API, not only does this affect every generate method, but anything that might call them. You have to thread the indent level to anything that might want to write code. It might be better to make it a global (i.e. - instance or module variable) value of some sort. Basically, this "trivial difference" needs to be taken into consideration when designing the system architecture. You came close to the real reason for rejecting this here:
When you defend a feature on the basis that "people will hardly ever use it, except for trivial pieces of code that don't need it", that speaks volumes.
Two use cases are at or over the edge. Writing in something that lets you embed python code? You arguably aren't writing python. Writing code that generates python? *You* may not be writing python at all, even if your code is. That leaves the case of turning statements into expressions. While suggestions/requests for similar things keep coming up, they aren't all that frequent, and the tools we have for that have been acceptable so far. While Nick is to be congratulated for providing a solution to that problem that's much better than it's predecessors, I don't think it's worth the complexity it looks like it would require in the compiler. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

On Sun, Dec 11, 2011 at 1:40 PM, Mike Meyer <mwm@mired.org> wrote:
Users generally aren't expected to read generated code. It's pretty ugly for systems that generate python now.
If the point really is just generated code, we should just invent some really ugly but easy-to-parse and 100%-guaranteed backwards compatible set of brackets for just that purpose. But I don't believe this is about generated code. -- --Guido van Rossum (python.org/~guido)

Yes, but with an appropriate design you can arrange for that to be done in only *one* place. There's no need for every method that generates code to know about indentation levels. For example, you can create a code-output-stream object that you pass to your code-generating methods. You give it begin_block() and end-block() methods that you call whenever you would otherwise have written opening and closing delimiters. It keeps track of the indentation level internally and emits the appropriate amount of whitespace before each line. -- Greg

On Mon, 12 Dec 2011 18:21:26 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Exactly. This change causes you to modify your architecture to deal with it. In my book, changes that cause you to rethink the architecture of your system aren't "trivial". <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Mike Meyer wrote:
Exactly. This change causes you to modify your architecture to deal with it.
It's not about "modifying" an architecture, it's about considering the requirements and designing an appropriate architecture from the beginning to accommodate them. If you set out to generate Python code, write most of your program and then say "Oh, bugger, what am I going to do about indentation?", then you just haven't thought far enough ahead. Also, the particular architecture I suggested was only an example. If you put your mind to it, you should be able to come up with many ways of skinning this snake. If you want to keep your generator style, you could yield special "indent" and "dedent" tokens that the top level recognises and does the appropriate thing with. You might even use special strings such as "{:" and ":}" embedded in the text to represent these. Then your code generating methods wouldn't look all that much different from how they would if you were generating the delimited form of python that you are arguing for... As a side note, I would argue that it's a good idea to adopt an architecture that can manage indentation in any case, whether you're generating indentation-sensitive code or not. I use this kind of architecture in Pyrex to generate C code, even though indentation isn't required in C. I do it because I want to generate properly-indented code anyway, for reasons of readability. Even if the output is normally never seen, it's a big help when debugging. -- Greg

On Sun, Dec 11, 2011 at 9:21 PM, Greg Ewing <greg.ewing@canterbury.ac.nz>wrote:
I've written this code (for HTML) and it's not only straightforward, it makes the code easier to write and more robust. And you can make the closing automatic with a context manager. Here's how you might write some HTML: with html_tag('table'): with html_tag('tr'): for x in data: with html_tag('td'): # do stuff with x On the other hand, this might not help with template languages as Nick points out: On Sun, Dec 11, 2011 at 2:30 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
While I thought his example was compelling at first, after thinking it through I realized that any existing template language that generates Python has to be aware of indentation and it's going to have to modify the indentation of the included Python code. So it would need to either read the indentation or read the {: and :} tokens. So what if we think in terms of modifying the template language instead of Python? In the code below, I've added two extra tokens to the template language: %{> which increases the indentation and <}% which decreases it. All other leading spaces would be stripped. Here's that example rewritten: <% if danger_level > 3: %{> <div class="alert"><% if danger_level == 5: %{>EXTREME <}% %>DANGER ALERT!</div> <% elif danger_level > 0: %{> <div>Some chance of danger</div> <% else: %{> <div>No danger</div> <}%> <% for a in ['cat', 'dog', 'rabbit']: %{> <h2><%= a %></h2> <p><%= describe_animal(a) %></p> <}%> This isn't a perfect design. In particular %{> wouldn't be necessary if the template language parses the embedded code, but it's sufficient to illustrate the point. --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com

On Mon, Dec 12, 2011 at 4:09 PM, Bruce Leban <bruce@leapyear.org> wrote:
Sure, but that's just perpetuating the status quo - you can fairly easily create a Python *inspired* templating language right now by building your suite delimeters into your template syntax (e.g. Django and Jinja2 favour explicit suite closing snippets like "{% endfor %}"). It isn't that Python *can't* be tunnelled through whitespace insensitive environments - it's that there's no *standard* mechanism for doing so, so everyone that has the need reinvents the wheel in their own idiosyncratic way. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Mon, Dec 12, 2011 at 4:47 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Sure, that would be one way to go. It doesn't give you multi-line lambda support or any of the other goodies, though. Personally, I don't intend to take this idea any further than the writeup I posted over on RTFD: http://readthedocs.org/docs/ncoghlan_devs-python-notes/en/latest/pep_ideas/s... If someone else wants to pursue it, either as a Python reformatting library or as an actual modification to the language grammar, more power to 'em. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

While typing this reply to the "Alternate Suite Delineation Syntax For Python?" thread. I realized that having things all on a single line isn't important or needed!!! Yes, it's not needed for templates, or code generation. So this is an alternate to the alternate. What is needed is a uniform way to represent indenting that is easy to transport to (ie paste into) code with different indent levels. INDENT LEVEL MARKERS: ---------------------- Offer a new way to spell <indent+1>, <indent+0>, and <indent-1>. To make that work, we need to use unique character sequences that aren't already used. After some thought, I came up with these indent-markers. /// # indent level +1 ;;; # indent level +0 \\\ # indent level -1 These are unique enough to be easy to see, and the tripled versions of them aren't really that bad as we already have triple quotes, and they would be less than the white space they are replacing in most cases. So this isn't brackets, or braces. It would be just an alternate spelling in already valid python. It should not effect the parser, grammar, or interpreter in any way if it is done in pythons tokenizer. :-) Reusing some previous examples. #-----------------------
def acc(n) {: s=n; return {: def (i) {: nonlocal s; s += i; return s}}}
#Standard python... def acc(n): s=n def add(i): nonlocal s s += i return s return add #Single line version... def acc(n): /// s=n ;;; def add(i): /// nonlocal s \ ;;; s+=i ;;; return s \\\ return add (The '\' character should still work.) #Transportable indent level version... This could also be valid... and could be transportable to any other indent level without having to edit it. (So would the above single line version by adding a ';;;' to the beginning.) ;;; def acc(n): # indent +0 /// s = n # indent +1 ;;; def add(i): # indent +0 /// nonlocal s # indent +1 ;;; s += i # indent +0 ;;; return s # indent +0 \\\ return add # indent -1 It has the nice properties of being uniform, and being able to be inserted easily into any existing code block without having to adjust the indent levels. I believe that these properties are more important than being able to put things on a single line and could be a very useful in code generating programs. ;-) It may be easy to implement. This could probably all be done in the tokinze.c get_tok() function, where it detects and converts white space to indention levels. Nothing else in python would need to be changed. The tokenizer already detects line continuations and different line endings, so this wouldn't be that out of place. Cheers, Ron

On Mon, Dec 12, 2011 at 04:07:13PM -0600, Ron Adam wrote:
[skip]
def acc(n): /// s=n ;;; def add(i): /// nonlocal s \ ;;; s+=i ;;; return s \\\ return add
from __future__ import brainfuck (I mean well-known esoteric programming language)? Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

I do not like it and I am not convinced this belongs to Python Yet you may like this other solution: exec(reindent("def f(a):; x=0; for i in range(a):; x+=i; pass; return x; print f(5)")) here reindent is a modification of the web2py reindent function (used in templates). - ; is converted to \n - loops are closed with pass - ifs are also closed with pass - def can be closed with pass or return - class must also end with pass In web2py-ese templates we would not use it like this. we would write instead: {{def f(a):}}{{x=0}}{{for i in range(a):}}{{x+=i}}{{pass}}{{return x}}{{=f(5)}} or break it into {{ def f(a): x=0 for i in range(a): x+=i pass return x = f(5) }} (indentation is irrelevant even within the {{code}}) On Dec 12, 2011, at 4:07 PM, Ron Adam wrote:

On 12/12/2011 22:07, Ron Adam wrote:
[snip] -1 If the problem is with leading whitespace being stripped, then what about the non-breaking space ("\xA0")? We all use Unicode now, don't we? :-) The bonus is that it still looks like a space.

On Mon, 2011-12-12 at 23:15 +0000, MRAB wrote:
Think bigger! ie... easy automated program content and construction. You have this string... foo_bit.py """ def foo(*args): result = <do something with args> return result x = foo(a, b, c) """ And you want it to be inserted in this string automatically with the least amount of processing. (with another program you have.) bar_bit.py """ ... def bar(a, b, c): ... <insert foo_bit.py here> ... return x ... """ (where ... means more lines of python code.) Now if you do a quick simple substitution you get. """ ... def bar(a, b, c): ... def foo(*args): result = <do something with args> return result x = foo(a, b, c) ... return x ... """ Which won't work and is why template programs need to do a lot of pre and post processing to get things right. If foo_bit.py was like this... """ ;;; def foo(*args): ;;; result = <do something with args> ;;; return result ;;; x = foo(a, b, c) """ Then you would get... """ ... def bar(a, b, c): ... ;;; def foo(*args): ;;; result = <do something with args> ;;; return result ;;; x = foo(a, b, c) ... return x ... """ And the tokenizer in python could read that directly as ... ... def bar(a, b, c): ... def foo(*args): result = <do something with args> return result x = foo(a, b, c) ... return x ... Which would make doing things like this really easy instead of really tricky. The indent level of bar.py won't matter because the indent marker ';;;' says to use the current level of indent before this line. Does that make more sense now? Cheers, Ron

On 13/12/2011 00:01, Ron Adam wrote:
OK, let's suppose that bar_bit.py is a template file with an insertion point: bar_bit.py """ ... def bar(a, b, c): ... <insert here> ... return x ... """ I would expect the inserted code to be indented by however much the "<insert here>" is indented: """ ... def bar(a, b, c): ... def foo(*args): result =<do something with args> return result x = foo(a, b, c) ... return x ... """
I still think it looks messy and that it's the responsibility of the templater to fix the indentation.

On Mon, Dec 12, 2011 at 9:24 PM, Terry Reedy <tjreedy@udel.edu> wrote:
No they wouldn't, these are too similar to the markers left behind by merge tools for unmergable sections. Those markers were chosen because they are unlikely to occur in regular code. Let's keep it that way. :-) -- --Guido van Rossum (python.org/~guido)

On 12/13/2011 10:53 AM, Guido van Rossum wrote:
My objection to these is that the interpretation is arbitrary; I happen to read them the wrong way.
Funny. I choose them for the same reason. >>> is also bad because some mail agents, like Thunderbird not only interpret initial runs of > as message quotes but replace them with various colored bars. So if this were a serious idea, I would propose something else like ->| -|- |<- But a moot point unless we actually do want a standardized transport encoding for indents and dedents. -- Terry Jan Reedy

On 15/12/2011 00:18, Greg Ewing wrote:
It annoys me too, so I went looking: Quote bars http://kb.mozillazine.org/Quote_bars On my PC the prefs.js file was in a subfolder of: C:\Documents and Settings\Administrator\Application Data\Thunderbird\Profiles and it now shows the ">" as ">".

On Wed, 2011-12-14 at 18:53 -0500, Terry Reedy wrote:
I think if there is enough interest in it, it could be done. It only changes the pre-tokenized representation of the language. To do braces correctly, it would require much deeper changes. My choices are based on both easy visibility and also what be nice for single line input. The ';;;' is consistent with the ';' in places where it is used. Triple characters are easier to type than odd sequences. I suppose I'd switch the slashes though so they follow the top down flow better. ;;; def acc(n): # indent +0 \\\ s = n # indent +1 ;;; def add(i): # indent +0 \\\ nonlocal s # indent +1 ;;; s += i # indent +0 ;;; return s # indent +0 /// return add # indent -1 cheers, Ron

On Thu, Dec 15, 2011 at 11:08 AM, Ron Adam <ron3200@gmail.com> wrote:
It only changes the pre-tokenized representation of the language. To do braces correctly, it would require much deeper changes.
This comment makes me think you may be working off a misunderstanding of the way Python's tokenisation works. Suites in Python's token stream are *already* explicitly delimited: "INDENT" and "DEDENT" tokens respectively mark the beginning and end of each suite. There's no such concept in the token stream as "don't change the indent level" - that's the assumed behaviour. So an explicitly delimited syntax that just offers an alternative way to get INDENT and DEDENT tokens into the stream would be fairly straightforward. You'd probably also want an explicit ";;" token to force a token.NEWLINE into the token stream. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 11:29 +1000, Nick Coghlan wrote:
In tokenizer.c there is the function tok_get() at line 1292. The first part of it counts spaces and/or tabs and checks for other things and then returns either DEDENT, INDENT, or possibly an ERRORTOKEN. Since this would not add any new tokens, I referred to it as a pretokenized representation. The way it would work is to detects these characters and use it to determine wheather to return DEDENT, or INDENT tokens in place of any surrounding white space immediately before or after it. If these are found in the middle of a line, it would backup with tok_backup(), to just before the token, and then ruturn a NEWLINE. On the next tok_get() call, it would repeat. It's would not be hard to do at all.
You are thinking of later in the chain I think. We doing the change before that is decided. Think of them as hints for the tokenizer. The "don't change the indent level" hint, ';;;' is just a hint to ignore the white space here and accept the previous level. Because of that, the text source is able to be white space insensitive as far as indent levels is concerned.
Right. :-)
You'd probably also want an explicit ";;" token to force a token.NEWLINE into the token stream.
That isn't needed. Any of these in the middle of a line will add a new line and back up, so the next call to tok_get() will find it, and so on. ;;; x = 3 ;;; y = 2 ;;; def add(x,y): \\\ return x + y So this would be converted as the tokenizer goes over it to.. x = 3 y = 2 def add(x,y): return x + y Line continuations '\' are handled in pretty much the same way, they just eat the next newline, and continue. There is no token for a line continuation. These would work on the same level, Cheers, Ron

On Thu, Dec 15, 2011 at 1:19 PM, Ron Adam <ron3200@gmail.com> wrote:
OK, take the way you're thinking (indent +1, indent 0, indent -1) and instead think in terms of starting a suite, terminating a statement and terminating a suite: /// -> {: ;;; -> ;; \\\ -> :} Now do you see why I'm saying you're needlessly complicating things? Suite delimiters and statement terminators (or separators) are the way full whitespace insensitivity is normally handled when designing a language syntax. There's no reason to get creative here when the standard terminology and conventions would work just fine. For example, it shouldn't be difficult to create a variant of the tokenize module's tokeniser that adds the following rules: {: -> emits OP(':'), NEWLINE, INDENT and increments the parenlevel ;; -> emits NEWLINE :} -> emits NEWLINE, DEDENT and decrements the parenlevel and a variant of the untokenizer() that looks ahead and *emits* those character sequences when applicable. That should be enough to let you use Python code in whitespace insensitive environments without changing the semantics: Encoding for transport: tokenize() normally, untokenize() with suite delimiters Decoding from transport: tokenize() with suite delimiter support, untokenize() normally Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 13:40 +1000, Nick Coghlan wrote:
The complicated part is in getting across a new idea. ;-)
I'm not referring to a whole new variant, Just a small tweak to the current one.
This is the same idea with different spelling in this case. I think yours would be a bit harder to implement, but not all that hard to do. The version I proposed would be even easier to do. Unless you intend to enforce matching '{:' and ':}''s, then it become a bit harder still. I'm not sure that is needed, but it may seem strange to users if you don't require them to match. At what point should it be a part of the grammar? Ok how about some examples... how would you notate this code bit so it can be pasted into an already existing file without having to re-edit it? ;;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break Lets say you are going to past in several locations in a file and those locations may have different indent levels. How would you write that using your suggestion so you don't have to change it in anyway for it to work. With my suggestion, that would just work no matter the source file indent level. (*) All you need is the line numbers to insert it at. I think with your idea, you would also need a place holder of some type. * Assuming the required names are present. ;-)
and a variant of the untokenizer() that looks ahead and *emits* those character sequences when applicable.
That shouldn't be a problem in either case.
That should be enough to let you use Python code in whitespace insensitive environments without changing the semantics:
Right, except I'd be happier with something that doesn't alter the visible characters, the colon included.
I think one of the differences is I'm proposing a mode that doesn't require the whole file to be in matching form. So the "tokenize() with suite delimiter support" would just be the same tokenize() that is normally used. And the "untokenize() with suite delimiters" would probably just be a file that you run on the source to get a delimited version. I'm not proposing any untokenize() support. But I'm not against it either. <shrug> I also think indent markers are more consistent with how people think when they are programming python. It matches how the blocks are defined. If this was a language where we already had braces, then I'd definitely be thinking the other way. :-) Cheers, Ron

On Thu, Dec 15, 2011 at 3:09 PM, Ron Adam <ron3200@gmail.com> wrote:
OK, I at least understand what you're trying to do now. However, I don't think you have a valid use case. What would a templating language based on your notation look like? How would you extend it to implement multi-line lambdas? If you can't cleanly do those two things, who cares if it lets you copy and paste snippets at arbitrary points in a Python file? What's the *functional* pay-off for the ugliness? (And I freely admit that my suggestion is also quite ugly. However, the status quo, with everyone inventing assorted templating languages for use with Python that are largely independent of Python itself already sucks) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 15:25 +1000, Nick Coghlan wrote:
Any program that can patch text files together. As to how it does it, it probably would use a script of some sort. This would just make it easier to by offering a way around indenting issues. Another use case is it makes transmitting python compatible code easier in a web page or email. The braces version would do that also. I think it could make python a bit more attractive for things like that. Where a app is transmitted and run at the destination. There are still the security issues to work out, but this would knock down one domino in that direction. For example they don't have to event ways to avoid the white space munging. Python could be represented as code blocks that might look like... <pycode = ";;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break"> Except much longer. In this case there isn't a need for it to be pretty. But there is a need for it to work correctly. I was thinking it might be possible to have the python shell echo the indent markes in the left side as it scroll lines up. Replacing the '>>>' and '...' with the respective markes as you enter the lines. And it could also take these if they are pasted in. Cutting and pasting from one shell to another could work in that case. As far as unglyness goes... is it really that bad? you can leave the white space in. The indent hints are used not the spaces in this case. ;;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break And keep in mind that this isn't something you will see in normal use. It's just available for when its needed. But if you run across code like this it will still work. Nothing special would need to be done to make it work.
How would you extend it to implement multi-line lambdas?
As I said, it doesn't change the language at all. So extending lambda is not in the scope of this topic. That would be something new and should be done separately in any case.
? See above.
If it was a big patch to many files, I would say it isn't worth it. But it would be a very small patch to only one file and wouldn't change the grammer, ast, or anything else. It's almost free. I think it may have a nice benefit for very little effort. But I think we should just wait a bit and see if there is more interest first. ;-) Cheers, Ron

There's no way *any* variant of this idea (regardless of spelling) is getting anywhere near the stdlib without a long history on PyPI as a popular templating and code embedding tool. -- Nick Coghlan (via Gmail on Android, so likely to be more terse than usual) On Dec 15, 2011 5:06 PM, "Ron Adam" <ron3200@gmail.com> wrote:

On Thu, Dec 15, 2011 at 12:25 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Guido actually endorsed ugly if it is just used for transport. That said, for embedding in a template, I would prefer to avoid ugly. But why does it have to be all punctuation? :indent 4 if is_frobbable(user, request): :indent 5 frob(request) :indent 4 else: :indent 5 spit_error("Can't frob here, mate!") The grammar would be that at the start of a (line, string, whatever record marker the embedding environment has), the literal ":indent" followed by whitespace followed by a decimal number <count> followed by whitespace followed by a line of python code would be equivalent to that same line of python code indented <count> times. Ideally, the actual leading whitespace would also demonstrate proper indenting, but it wouldn't *need* to, so the code would be robust against transmission. -jJ

On Thu, Dec 15, 2011 at 3:09 PM, Ron Adam <ron3200@gmail.com> wrote:
Forget to answer this bit: I wouldn't. I'd use a templating language, that's what they're for. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Dec 15, 2011 at 9:53 AM, Terry Reedy <tjreedy@udel.edu> wrote:
But a moot point unless we actually do want a standardized transport encoding for indents and dedents.
And if we *were* going to do that, it's far more sensible to use a scheme that is at least *theoretically* open to the possibility of eventually allowing suite expressions. For the record, if anyone really wants to play with this, I'd start with the tokenize module, and figure out what tweaks are needed to create the following mappings between the source code and the token stream: "{:" <-> token.OP(":"), token.NEWLINE, token.INDENT ":}" <-> token.NEWLINE, token.DEDENT Then you can do source-source transforms by taking the output of the tweaked tokenizer and feeding it to the standard one and vice-versa. That's should be all you really need to create a delimited syntax variant. Things only get complex if you try to turn that delimited syntax variant into multi-line lambda support. P.S. See http://bugs.python.org/issue2134 in regard to why you can't use token.COLON for this -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Ron Adam wrote:
To allow easy use of python in environments which aren't space-sensitive, wouldn't another solution be to have some sort of non-space character which could act as leading spaces, e.g. def foo(): ;;a = 0 ;;for i in range(10): ;;;;a += i ;;return a Of course this wouldn't work for enviroments which lose or cannot contain line breaks, however. Jeremy

On Thu, Dec 15, 2011 at 12:11 PM, Jeremy Sanders <jeremy@jeremysanders.net>wrote:
Another solution - the templating language for bottle.py requires every block opened to be closed with an "end". Eg: if name: print name else: input("your name please:") __end__ Or we could use a special character for that... http://bottlepy.org/docs/dev/stpl.html#embedded-python-code --Yuval

On Thu, Dec 15, 2011 at 10:11:01AM +0000, Jeremy Sanders wrote:
<code> <line indent="0">def foo():</line> <line indent="1">a = 0</line> <line indent="1">for i in range(10):</line> <line indent="2">a += i</line> <line indent="1">return a</line> </code> ;-) Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On Thu, Dec 15, 2011 at 5:31 AM, Jeremy Sanders <jeremy@jeremysanders.net> wrote:
This would be awkward when the code is supposed to produce an attribute value, so that entire elements are inside an element-open tag.
Nice. I'd like the option of JSON too, please, though the brackets might be a bit confusing :-)
If you're willing to wrap each line in a string, then leading whitespace will already be preserved. If you're not willing to wrap each line in a string, then it isn't clear how JSON would represent the code part, regardless of how indention is conveyed. -jJ

Need a new why of indentation? (V) (;,,;) (V) Why not Zoidberg?? Seriously though, I don't think something like this will be ever accepted. It totally goes against pythons idea behind indentation. On 12/12/2011 11:07 PM, Ron Adam wrote:

On 2011-12-11, at 01:16 , Mike Meyer wrote: pyc files directly? That would avoid the issue would it not? And if users are supposed to edit (therefore read) the generated code, wouldn't you want to make the file actually readable, therefore use indentation-based blocks?
The short version is: http://www.haskell.org/onlinereport/lexemes.html#lexemes-layout * when a brace is omitted after a `where`, `let`, `of` or `do` keyword, a brace is inserted and the indentation of the next lexeme is remembered * if the next lexeme is indented below the keyword, immediately insert a closing brace (before the lexeme itself) * otherwise, for each subsequent line - if it contains only whitespace or is indented further than the current level, continue the current expression - if it is indented at the current level, insert a semicolon - if it is indented below the current level, insert a closing brace You can find the complete and more formal definition at http://www.haskell.org/onlinereport/syntax-iso.html#layout

On 12/10/2011 12:06 AM, Nick Coghlan wrote:
I believe you mean return the value of the last statement that is executed, as in the try-except example or the conditional example: {: if a {:b} else {:c}} I must congratulate you, Nick, for being the first person in my 14 years with Python to make a brace idea look half-way sensible. I can see that algorithmically generated code (which includes templates) might be a real use case. -- Terry Jan Reedy

On Sun, Dec 11, 2011 at 10:46 AM, Terry Reedy <tjreedy@udel.edu> wrote:
Indeed, that was what I meant, albeit not what I wrote :)
I actually surprised myself - I started writing that purely as a devil's advocate "if you're going to rant about something to that extent, you may as well *try* to be constructive" post, but the end result was significantly less terrible than I originally expected :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sat, 2011-12-10 at 15:06 +1000, Nick Coghlan wrote:
;-) In your example returning an unnamed function is a new feature in addition to the braces. It would probably need to be more like this... def acc(n){:s=n; def add(i){:nonlocal s; s+=i; return s};return add} There are a couple of issues to work out. Statements (def, class, for, while, and with) don't work after simi-colons. There may be others. Outside those issues, all you really need is an exit suit token. The colon can still be the open suit. Two semi-colons could do it. def acc(n):s=n; def add(i):nonlocal s; s+=i; return s;; return add The real issues are in getting the statements to work. Cheers, Ron

On 10 December 2011 05:06, Nick Coghlan <ncoghlan@gmail.com> wrote:
This, along with the original posting on python-dev, is a spectacular example of how to propose a new feature, and how not to. It would make an excellent example for a HOWTO document on proposing changes (with the OP's permission - he might not like his posting being held op as the canonical "how not to do this" example...) I barely read the original posting, even though I tried to follow it - my mind was screaming "ain't never going to happen" at me throughout. And yet Nick's rewording is pretty persuasive, I can see the benefits and the power of the idea. My only major objection is that the resulting code is pretty ugly. I could easily imagine something along these lines being accepted into Python. Thanks, Nick, for doing this - and Cedric for the original idea, even though I didn't appreciate it at the time :-) It certainly got me thinking. As regards the actual proposal, some comments: - It's got serious power (if, try and with expressions, etc etc) - It would answer a number of requests that keep coming up over and over again. - I suspect the semantics in corner cases will be hard to pin down, but the basic principle looks simple enough - The code is pretty ugly, and certainly an attractive nuisance for obfuscated code, but I can't think of a better syntax - Given that it effectively replaces lambdas and if expressions, it partly violates OOWTDI - and it does so in a way that could encourage people to still propose try or with expressions ("they are as common as conditionals, so why not?") Paul.

On 2011-12-10, at 12:32 , Paul Moore wrote:
An other possibility (with some precedent in generator comprehensions) would be to declare current statements to be expressions (and define their expressive semantics) and surround them with parens for disambiguation purposes when necessary.
On 2011-12-10, at 06:06 , Nick Coghlan wrote:

On Sat, 10 Dec 2011 15:06:13 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
Since you sent it to Python-ideas, I'm going to assume you were at least semi-serious. So let me add the use case I came up with while reading the original post, also often seen in template languages: Writing code to generate python. With significant whitespace, you have to keep explicit track of the current nesting level in order to generate the appropriate indent. With real delimiters, you can drop that. Compare: def generate(self): yield '{keyword} {expr}{:'.format(keyword=self.name, expr=self.expresion) for s in self.suite: for l in s.generate(): yield l With: def generate(self, level): yield '{0}{keyword} {expr}:'.format(' ' * level, keyword=self.name, expr=self.expresion) for s in self.suite: for l in s.generate(level + 1): yield l A minor improvement in just that one method, but it will show up in every generate method of every class, making it significant in total.
Applying the excellent set of criteria suggested by Mike Meyer:
"Suggested" may be a bit to much credit. I've observed the community applying these criteria to pretty much every idea. Some ideas get adopted in spite of failing one or more of them, but a *lot* of ideas get dropped after failing one of them. In this case: 1. What's the use case? Writing code that's going to write python code.
2. Does it make such code more readable?
It makes the code generating code less complicated, which means more readable.
I think not. As someone else observed, Haskell has both options available - and the rules for the indented version are a lot less straightforward than they are in Python (at least I haven't seen as clean a statement of them). About the only use of the delimited version in real code is turning a short suite into a one-line suite. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Mike Meyer wrote:
That's hardly an onerous requirement. As you show, the two code snippets are almost identical, except that the indent version takes an explicit level argument, while your braces version neglects to close suites at all. A fair comparison should show the close-suite code, even if it is only a single line.
I guess you would need to yield '}' after the outer for loop.
I wouldn't use the term "minor improvement" so much as "trivial difference". But even if I grant that it is an improvement, how many generate methods do you expect there to be? Presumably most of them will inherit their generate method from one or two parent classes. Even if every block keyword gets an independent copy in violation of DRY, there's still fewer than a dozen classes needed: def class for while if elif else try except finally with. So the total significance is still likely to be small. But wait... now you have your code generator, which generates ugly, unreadable code. Code needs to be read more often than written, even generated code, so now you need a code beautifier to reformat it using indentation for the human reader. Which means keeping track of the indent level. You're right back where you started, only worse, because now you have to parse the generated code to reformat it, instead of formatting it correctly the first time. So to save a dozen lines in your code generator, you end up with an extra hundred lines in a code beautifier. A classic example of code pessimation (of LOC rather than speed in this case). I think it is suggestive that we will need to warn people not to use this hypothetical syntax. As Nick wrote:
People who don't care about maintainability don't care about readability and they certainly don't read style guides unless they are forced to.
When you defend a feature on the basis that "people will hardly ever use it, except for trivial pieces of code that don't need it", that speaks volumes. -- Steven

On Sun, Dec 11, 2011 at 11:55 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Indeed, that's a reason I didn't use straight up code generation as my motivating use case - the significant whitespace simply isn't that painful in that case. The reason significant leading whitespace can be a PITA is due to two main circumstances: 1. Attempting to transport it through a channel that either strips leading and trailing whitespace from lines, or else consolidates certain whitespace sequences into a single whitespace character (generally sequences of spaces and tabs becoming a single space). Python source code simply cannot be passed through such channels correctly - if they don't offer an escaping mechanism, or that mechanism is not applied correctly, the code *will* be corrupted. Explicitly delimited code, on the other hand, can be passed through without semantic alteration (even if the details of the whitespace change) and a pretty printer can fix it at the far end. 2. Attempting to transport it through a channel where leading whitespace already has another meaning. This comes up primarily with templating languages - your whitespace is generally part of the template output, so it becomes problematic to break up your Python code across multiple code snippets while keeping the suite groupings clear. With explicit delimiters, though, you can just ignore the template parts, and pretend the code snippets are all part of a single string (the following is based on an example a colleague sent me [1], with the closing delimiter adjusted to mirror the opening one as a couple of folks suggested): <% if danger_level > 3 {: %> <div class="alert"><% if danger_level == 5 {: %>EXTREME <% :} %>DANGER ALERT!</div> <% elif danger_level > 0 {: %> <div>Some chance of danger</div> <% else {: %> <div>No danger</div> <% :} %> <% for a in ['cat', 'dog', 'rabbit'] {: %> <h2><%= a %></h2> <p><%= describe_animal(a) %></p> <% :} %> [1] https://gist.github.com/1455210 Is such code as beautiful and readable as normal Python code? No, of course not. But it would serve a purpose in defining a *standard* way to embed Python code in environments where the leading whitespace causes problems. In terms of how such a delimited syntax would relate to currently legal Python code, the transformation is basically the reverse of the Haskell one: 1. If you encounter a ":" to open a new suite, replace it and any trailing whitespace with "{:". 2. All whitespace between statements in such a suite is replaced with ";" characters 3. Any trailing whitespace after the last statement in the suite is replaced with ":};" The rest of the changes (i.e. a suite's expression value being the result of the last statement executed, anonymous function and class definitions) then follow from the fact that you now have a suite syntax that can potentially be embedded as an expression, so a range of new possibilities open up. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sun, 11 Dec 2011 09:44:17 +0100 Masklinn <masklinn@masklinn.net> wrote:
Users generally aren't expected to read generated code. It's pretty ugly for systems that generate python now. The reason you generate python instead of pyc is that backward compatibility of python source is a priority for python development, so with a little care your generated code will work across multiple versions of python. The same is not true for byte code files (and if that's changed with 3.x, I couldn't find anything saying so), so your code generator is liable to depend on the version of the target python interpreter. On Sun, 11 Dec 2011 12:55:51 +1100 Steven D'Aprano <steve@pearwood.info> wrote:
Correct. I thought it, but forgot to add it.
I wouldn't refer to any change that involves adding a variable as "trivial". Especially one that's part of the API. Part of the issue is that I understated the problem - this change is ubiquitous, and large enough that it adds up.
But even if I grant that it is an improvement, how many generate methods do you expect there to be?
Because it's in the API, not only does this affect every generate method, but anything that might call them. You have to thread the indent level to anything that might want to write code. It might be better to make it a global (i.e. - instance or module variable) value of some sort. Basically, this "trivial difference" needs to be taken into consideration when designing the system architecture. You came close to the real reason for rejecting this here:
When you defend a feature on the basis that "people will hardly ever use it, except for trivial pieces of code that don't need it", that speaks volumes.
Two use cases are at or over the edge. Writing in something that lets you embed python code? You arguably aren't writing python. Writing code that generates python? *You* may not be writing python at all, even if your code is. That leaves the case of turning statements into expressions. While suggestions/requests for similar things keep coming up, they aren't all that frequent, and the tools we have for that have been acceptable so far. While Nick is to be congratulated for providing a solution to that problem that's much better than it's predecessors, I don't think it's worth the complexity it looks like it would require in the compiler. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

On Sun, Dec 11, 2011 at 1:40 PM, Mike Meyer <mwm@mired.org> wrote:
Users generally aren't expected to read generated code. It's pretty ugly for systems that generate python now.
If the point really is just generated code, we should just invent some really ugly but easy-to-parse and 100%-guaranteed backwards compatible set of brackets for just that purpose. But I don't believe this is about generated code. -- --Guido van Rossum (python.org/~guido)

Yes, but with an appropriate design you can arrange for that to be done in only *one* place. There's no need for every method that generates code to know about indentation levels. For example, you can create a code-output-stream object that you pass to your code-generating methods. You give it begin_block() and end-block() methods that you call whenever you would otherwise have written opening and closing delimiters. It keeps track of the indentation level internally and emits the appropriate amount of whitespace before each line. -- Greg

On Mon, 12 Dec 2011 18:21:26 +1300 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Exactly. This change causes you to modify your architecture to deal with it. In my book, changes that cause you to rethink the architecture of your system aren't "trivial". <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Mike Meyer wrote:
Exactly. This change causes you to modify your architecture to deal with it.
It's not about "modifying" an architecture, it's about considering the requirements and designing an appropriate architecture from the beginning to accommodate them. If you set out to generate Python code, write most of your program and then say "Oh, bugger, what am I going to do about indentation?", then you just haven't thought far enough ahead. Also, the particular architecture I suggested was only an example. If you put your mind to it, you should be able to come up with many ways of skinning this snake. If you want to keep your generator style, you could yield special "indent" and "dedent" tokens that the top level recognises and does the appropriate thing with. You might even use special strings such as "{:" and ":}" embedded in the text to represent these. Then your code generating methods wouldn't look all that much different from how they would if you were generating the delimited form of python that you are arguing for... As a side note, I would argue that it's a good idea to adopt an architecture that can manage indentation in any case, whether you're generating indentation-sensitive code or not. I use this kind of architecture in Pyrex to generate C code, even though indentation isn't required in C. I do it because I want to generate properly-indented code anyway, for reasons of readability. Even if the output is normally never seen, it's a big help when debugging. -- Greg

On Sun, Dec 11, 2011 at 9:21 PM, Greg Ewing <greg.ewing@canterbury.ac.nz>wrote:
I've written this code (for HTML) and it's not only straightforward, it makes the code easier to write and more robust. And you can make the closing automatic with a context manager. Here's how you might write some HTML: with html_tag('table'): with html_tag('tr'): for x in data: with html_tag('td'): # do stuff with x On the other hand, this might not help with template languages as Nick points out: On Sun, Dec 11, 2011 at 2:30 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
While I thought his example was compelling at first, after thinking it through I realized that any existing template language that generates Python has to be aware of indentation and it's going to have to modify the indentation of the included Python code. So it would need to either read the indentation or read the {: and :} tokens. So what if we think in terms of modifying the template language instead of Python? In the code below, I've added two extra tokens to the template language: %{> which increases the indentation and <}% which decreases it. All other leading spaces would be stripped. Here's that example rewritten: <% if danger_level > 3: %{> <div class="alert"><% if danger_level == 5: %{>EXTREME <}% %>DANGER ALERT!</div> <% elif danger_level > 0: %{> <div>Some chance of danger</div> <% else: %{> <div>No danger</div> <}%> <% for a in ['cat', 'dog', 'rabbit']: %{> <h2><%= a %></h2> <p><%= describe_animal(a) %></p> <}%> This isn't a perfect design. In particular %{> wouldn't be necessary if the template language parses the embedded code, but it's sufficient to illustrate the point. --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com

On Mon, Dec 12, 2011 at 4:09 PM, Bruce Leban <bruce@leapyear.org> wrote:
Sure, but that's just perpetuating the status quo - you can fairly easily create a Python *inspired* templating language right now by building your suite delimeters into your template syntax (e.g. Django and Jinja2 favour explicit suite closing snippets like "{% endfor %}"). It isn't that Python *can't* be tunnelled through whitespace insensitive environments - it's that there's no *standard* mechanism for doing so, so everyone that has the need reinvents the wheel in their own idiosyncratic way. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Mon, Dec 12, 2011 at 4:47 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Sure, that would be one way to go. It doesn't give you multi-line lambda support or any of the other goodies, though. Personally, I don't intend to take this idea any further than the writeup I posted over on RTFD: http://readthedocs.org/docs/ncoghlan_devs-python-notes/en/latest/pep_ideas/s... If someone else wants to pursue it, either as a Python reformatting library or as an actual modification to the language grammar, more power to 'em. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

While typing this reply to the "Alternate Suite Delineation Syntax For Python?" thread. I realized that having things all on a single line isn't important or needed!!! Yes, it's not needed for templates, or code generation. So this is an alternate to the alternate. What is needed is a uniform way to represent indenting that is easy to transport to (ie paste into) code with different indent levels. INDENT LEVEL MARKERS: ---------------------- Offer a new way to spell <indent+1>, <indent+0>, and <indent-1>. To make that work, we need to use unique character sequences that aren't already used. After some thought, I came up with these indent-markers. /// # indent level +1 ;;; # indent level +0 \\\ # indent level -1 These are unique enough to be easy to see, and the tripled versions of them aren't really that bad as we already have triple quotes, and they would be less than the white space they are replacing in most cases. So this isn't brackets, or braces. It would be just an alternate spelling in already valid python. It should not effect the parser, grammar, or interpreter in any way if it is done in pythons tokenizer. :-) Reusing some previous examples. #-----------------------
def acc(n) {: s=n; return {: def (i) {: nonlocal s; s += i; return s}}}
#Standard python... def acc(n): s=n def add(i): nonlocal s s += i return s return add #Single line version... def acc(n): /// s=n ;;; def add(i): /// nonlocal s \ ;;; s+=i ;;; return s \\\ return add (The '\' character should still work.) #Transportable indent level version... This could also be valid... and could be transportable to any other indent level without having to edit it. (So would the above single line version by adding a ';;;' to the beginning.) ;;; def acc(n): # indent +0 /// s = n # indent +1 ;;; def add(i): # indent +0 /// nonlocal s # indent +1 ;;; s += i # indent +0 ;;; return s # indent +0 \\\ return add # indent -1 It has the nice properties of being uniform, and being able to be inserted easily into any existing code block without having to adjust the indent levels. I believe that these properties are more important than being able to put things on a single line and could be a very useful in code generating programs. ;-) It may be easy to implement. This could probably all be done in the tokinze.c get_tok() function, where it detects and converts white space to indention levels. Nothing else in python would need to be changed. The tokenizer already detects line continuations and different line endings, so this wouldn't be that out of place. Cheers, Ron

On Mon, Dec 12, 2011 at 04:07:13PM -0600, Ron Adam wrote:
[skip]
def acc(n): /// s=n ;;; def add(i): /// nonlocal s \ ;;; s+=i ;;; return s \\\ return add
from __future__ import brainfuck (I mean well-known esoteric programming language)? Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

I do not like it and I am not convinced this belongs to Python Yet you may like this other solution: exec(reindent("def f(a):; x=0; for i in range(a):; x+=i; pass; return x; print f(5)")) here reindent is a modification of the web2py reindent function (used in templates). - ; is converted to \n - loops are closed with pass - ifs are also closed with pass - def can be closed with pass or return - class must also end with pass In web2py-ese templates we would not use it like this. we would write instead: {{def f(a):}}{{x=0}}{{for i in range(a):}}{{x+=i}}{{pass}}{{return x}}{{=f(5)}} or break it into {{ def f(a): x=0 for i in range(a): x+=i pass return x = f(5) }} (indentation is irrelevant even within the {{code}}) On Dec 12, 2011, at 4:07 PM, Ron Adam wrote:

On 12/12/2011 22:07, Ron Adam wrote:
[snip] -1 If the problem is with leading whitespace being stripped, then what about the non-breaking space ("\xA0")? We all use Unicode now, don't we? :-) The bonus is that it still looks like a space.

On Mon, 2011-12-12 at 23:15 +0000, MRAB wrote:
Think bigger! ie... easy automated program content and construction. You have this string... foo_bit.py """ def foo(*args): result = <do something with args> return result x = foo(a, b, c) """ And you want it to be inserted in this string automatically with the least amount of processing. (with another program you have.) bar_bit.py """ ... def bar(a, b, c): ... <insert foo_bit.py here> ... return x ... """ (where ... means more lines of python code.) Now if you do a quick simple substitution you get. """ ... def bar(a, b, c): ... def foo(*args): result = <do something with args> return result x = foo(a, b, c) ... return x ... """ Which won't work and is why template programs need to do a lot of pre and post processing to get things right. If foo_bit.py was like this... """ ;;; def foo(*args): ;;; result = <do something with args> ;;; return result ;;; x = foo(a, b, c) """ Then you would get... """ ... def bar(a, b, c): ... ;;; def foo(*args): ;;; result = <do something with args> ;;; return result ;;; x = foo(a, b, c) ... return x ... """ And the tokenizer in python could read that directly as ... ... def bar(a, b, c): ... def foo(*args): result = <do something with args> return result x = foo(a, b, c) ... return x ... Which would make doing things like this really easy instead of really tricky. The indent level of bar.py won't matter because the indent marker ';;;' says to use the current level of indent before this line. Does that make more sense now? Cheers, Ron

On 13/12/2011 00:01, Ron Adam wrote:
OK, let's suppose that bar_bit.py is a template file with an insertion point: bar_bit.py """ ... def bar(a, b, c): ... <insert here> ... return x ... """ I would expect the inserted code to be indented by however much the "<insert here>" is indented: """ ... def bar(a, b, c): ... def foo(*args): result =<do something with args> return result x = foo(a, b, c) ... return x ... """
I still think it looks messy and that it's the responsibility of the templater to fix the indentation.

On Mon, Dec 12, 2011 at 9:24 PM, Terry Reedy <tjreedy@udel.edu> wrote:
No they wouldn't, these are too similar to the markers left behind by merge tools for unmergable sections. Those markers were chosen because they are unlikely to occur in regular code. Let's keep it that way. :-) -- --Guido van Rossum (python.org/~guido)

On 12/13/2011 10:53 AM, Guido van Rossum wrote:
My objection to these is that the interpretation is arbitrary; I happen to read them the wrong way.
Funny. I choose them for the same reason. >>> is also bad because some mail agents, like Thunderbird not only interpret initial runs of > as message quotes but replace them with various colored bars. So if this were a serious idea, I would propose something else like ->| -|- |<- But a moot point unless we actually do want a standardized transport encoding for indents and dedents. -- Terry Jan Reedy

On 15/12/2011 00:18, Greg Ewing wrote:
It annoys me too, so I went looking: Quote bars http://kb.mozillazine.org/Quote_bars On my PC the prefs.js file was in a subfolder of: C:\Documents and Settings\Administrator\Application Data\Thunderbird\Profiles and it now shows the ">" as ">".

On Wed, 2011-12-14 at 18:53 -0500, Terry Reedy wrote:
I think if there is enough interest in it, it could be done. It only changes the pre-tokenized representation of the language. To do braces correctly, it would require much deeper changes. My choices are based on both easy visibility and also what be nice for single line input. The ';;;' is consistent with the ';' in places where it is used. Triple characters are easier to type than odd sequences. I suppose I'd switch the slashes though so they follow the top down flow better. ;;; def acc(n): # indent +0 \\\ s = n # indent +1 ;;; def add(i): # indent +0 \\\ nonlocal s # indent +1 ;;; s += i # indent +0 ;;; return s # indent +0 /// return add # indent -1 cheers, Ron

On Thu, Dec 15, 2011 at 11:08 AM, Ron Adam <ron3200@gmail.com> wrote:
It only changes the pre-tokenized representation of the language. To do braces correctly, it would require much deeper changes.
This comment makes me think you may be working off a misunderstanding of the way Python's tokenisation works. Suites in Python's token stream are *already* explicitly delimited: "INDENT" and "DEDENT" tokens respectively mark the beginning and end of each suite. There's no such concept in the token stream as "don't change the indent level" - that's the assumed behaviour. So an explicitly delimited syntax that just offers an alternative way to get INDENT and DEDENT tokens into the stream would be fairly straightforward. You'd probably also want an explicit ";;" token to force a token.NEWLINE into the token stream. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 11:29 +1000, Nick Coghlan wrote:
In tokenizer.c there is the function tok_get() at line 1292. The first part of it counts spaces and/or tabs and checks for other things and then returns either DEDENT, INDENT, or possibly an ERRORTOKEN. Since this would not add any new tokens, I referred to it as a pretokenized representation. The way it would work is to detects these characters and use it to determine wheather to return DEDENT, or INDENT tokens in place of any surrounding white space immediately before or after it. If these are found in the middle of a line, it would backup with tok_backup(), to just before the token, and then ruturn a NEWLINE. On the next tok_get() call, it would repeat. It's would not be hard to do at all.
You are thinking of later in the chain I think. We doing the change before that is decided. Think of them as hints for the tokenizer. The "don't change the indent level" hint, ';;;' is just a hint to ignore the white space here and accept the previous level. Because of that, the text source is able to be white space insensitive as far as indent levels is concerned.
Right. :-)
You'd probably also want an explicit ";;" token to force a token.NEWLINE into the token stream.
That isn't needed. Any of these in the middle of a line will add a new line and back up, so the next call to tok_get() will find it, and so on. ;;; x = 3 ;;; y = 2 ;;; def add(x,y): \\\ return x + y So this would be converted as the tokenizer goes over it to.. x = 3 y = 2 def add(x,y): return x + y Line continuations '\' are handled in pretty much the same way, they just eat the next newline, and continue. There is no token for a line continuation. These would work on the same level, Cheers, Ron

On Thu, Dec 15, 2011 at 1:19 PM, Ron Adam <ron3200@gmail.com> wrote:
OK, take the way you're thinking (indent +1, indent 0, indent -1) and instead think in terms of starting a suite, terminating a statement and terminating a suite: /// -> {: ;;; -> ;; \\\ -> :} Now do you see why I'm saying you're needlessly complicating things? Suite delimiters and statement terminators (or separators) are the way full whitespace insensitivity is normally handled when designing a language syntax. There's no reason to get creative here when the standard terminology and conventions would work just fine. For example, it shouldn't be difficult to create a variant of the tokenize module's tokeniser that adds the following rules: {: -> emits OP(':'), NEWLINE, INDENT and increments the parenlevel ;; -> emits NEWLINE :} -> emits NEWLINE, DEDENT and decrements the parenlevel and a variant of the untokenizer() that looks ahead and *emits* those character sequences when applicable. That should be enough to let you use Python code in whitespace insensitive environments without changing the semantics: Encoding for transport: tokenize() normally, untokenize() with suite delimiters Decoding from transport: tokenize() with suite delimiter support, untokenize() normally Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 13:40 +1000, Nick Coghlan wrote:
The complicated part is in getting across a new idea. ;-)
I'm not referring to a whole new variant, Just a small tweak to the current one.
This is the same idea with different spelling in this case. I think yours would be a bit harder to implement, but not all that hard to do. The version I proposed would be even easier to do. Unless you intend to enforce matching '{:' and ':}''s, then it become a bit harder still. I'm not sure that is needed, but it may seem strange to users if you don't require them to match. At what point should it be a part of the grammar? Ok how about some examples... how would you notate this code bit so it can be pasted into an already existing file without having to re-edit it? ;;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break Lets say you are going to past in several locations in a file and those locations may have different indent levels. How would you write that using your suggestion so you don't have to change it in anyway for it to work. With my suggestion, that would just work no matter the source file indent level. (*) All you need is the line numbers to insert it at. I think with your idea, you would also need a place holder of some type. * Assuming the required names are present. ;-)
and a variant of the untokenizer() that looks ahead and *emits* those character sequences when applicable.
That shouldn't be a problem in either case.
That should be enough to let you use Python code in whitespace insensitive environments without changing the semantics:
Right, except I'd be happier with something that doesn't alter the visible characters, the colon included.
I think one of the differences is I'm proposing a mode that doesn't require the whole file to be in matching form. So the "tokenize() with suite delimiter support" would just be the same tokenize() that is normally used. And the "untokenize() with suite delimiters" would probably just be a file that you run on the source to get a delimited version. I'm not proposing any untokenize() support. But I'm not against it either. <shrug> I also think indent markers are more consistent with how people think when they are programming python. It matches how the blocks are defined. If this was a language where we already had braces, then I'd definitely be thinking the other way. :-) Cheers, Ron

On Thu, Dec 15, 2011 at 3:09 PM, Ron Adam <ron3200@gmail.com> wrote:
OK, I at least understand what you're trying to do now. However, I don't think you have a valid use case. What would a templating language based on your notation look like? How would you extend it to implement multi-line lambdas? If you can't cleanly do those two things, who cares if it lets you copy and paste snippets at arbitrary points in a Python file? What's the *functional* pay-off for the ugliness? (And I freely admit that my suggestion is also quite ugly. However, the status quo, with everyone inventing assorted templating languages for use with Python that are largely independent of Python itself already sucks) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, 2011-12-15 at 15:25 +1000, Nick Coghlan wrote:
Any program that can patch text files together. As to how it does it, it probably would use a script of some sort. This would just make it easier to by offering a way around indenting issues. Another use case is it makes transmitting python compatible code easier in a web page or email. The braces version would do that also. I think it could make python a bit more attractive for things like that. Where a app is transmitted and run at the destination. There are still the security issues to work out, but this would knock down one domino in that direction. For example they don't have to event ways to avoid the white space munging. Python could be represented as code blocks that might look like... <pycode = ";;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break"> Except much longer. In this case there isn't a need for it to be pretty. But there is a need for it to work correctly. I was thinking it might be possible to have the python shell echo the indent markes in the left side as it scroll lines up. Replacing the '>>>' and '...' with the respective markes as you enter the lines. And it could also take these if they are pasted in. Cutting and pasting from one shell to another could work in that case. As far as unglyness goes... is it really that bad? you can leave the white space in. The indent hints are used not the spaces in this case. ;;; for x in items: \\\ sum = op(x) ;;; if sum > MAX: \\\ break And keep in mind that this isn't something you will see in normal use. It's just available for when its needed. But if you run across code like this it will still work. Nothing special would need to be done to make it work.
How would you extend it to implement multi-line lambdas?
As I said, it doesn't change the language at all. So extending lambda is not in the scope of this topic. That would be something new and should be done separately in any case.
? See above.
If it was a big patch to many files, I would say it isn't worth it. But it would be a very small patch to only one file and wouldn't change the grammer, ast, or anything else. It's almost free. I think it may have a nice benefit for very little effort. But I think we should just wait a bit and see if there is more interest first. ;-) Cheers, Ron

There's no way *any* variant of this idea (regardless of spelling) is getting anywhere near the stdlib without a long history on PyPI as a popular templating and code embedding tool. -- Nick Coghlan (via Gmail on Android, so likely to be more terse than usual) On Dec 15, 2011 5:06 PM, "Ron Adam" <ron3200@gmail.com> wrote:

On Thu, Dec 15, 2011 at 12:25 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Guido actually endorsed ugly if it is just used for transport. That said, for embedding in a template, I would prefer to avoid ugly. But why does it have to be all punctuation? :indent 4 if is_frobbable(user, request): :indent 5 frob(request) :indent 4 else: :indent 5 spit_error("Can't frob here, mate!") The grammar would be that at the start of a (line, string, whatever record marker the embedding environment has), the literal ":indent" followed by whitespace followed by a decimal number <count> followed by whitespace followed by a line of python code would be equivalent to that same line of python code indented <count> times. Ideally, the actual leading whitespace would also demonstrate proper indenting, but it wouldn't *need* to, so the code would be robust against transmission. -jJ

On Thu, Dec 15, 2011 at 3:09 PM, Ron Adam <ron3200@gmail.com> wrote:
Forget to answer this bit: I wouldn't. I'd use a templating language, that's what they're for. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Dec 15, 2011 at 9:53 AM, Terry Reedy <tjreedy@udel.edu> wrote:
But a moot point unless we actually do want a standardized transport encoding for indents and dedents.
And if we *were* going to do that, it's far more sensible to use a scheme that is at least *theoretically* open to the possibility of eventually allowing suite expressions. For the record, if anyone really wants to play with this, I'd start with the tokenize module, and figure out what tweaks are needed to create the following mappings between the source code and the token stream: "{:" <-> token.OP(":"), token.NEWLINE, token.INDENT ":}" <-> token.NEWLINE, token.DEDENT Then you can do source-source transforms by taking the output of the tweaked tokenizer and feeding it to the standard one and vice-versa. That's should be all you really need to create a delimited syntax variant. Things only get complex if you try to turn that delimited syntax variant into multi-line lambda support. P.S. See http://bugs.python.org/issue2134 in regard to why you can't use token.COLON for this -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Ron Adam wrote:
To allow easy use of python in environments which aren't space-sensitive, wouldn't another solution be to have some sort of non-space character which could act as leading spaces, e.g. def foo(): ;;a = 0 ;;for i in range(10): ;;;;a += i ;;return a Of course this wouldn't work for enviroments which lose or cannot contain line breaks, however. Jeremy

On Thu, Dec 15, 2011 at 12:11 PM, Jeremy Sanders <jeremy@jeremysanders.net>wrote:
Another solution - the templating language for bottle.py requires every block opened to be closed with an "end". Eg: if name: print name else: input("your name please:") __end__ Or we could use a special character for that... http://bottlepy.org/docs/dev/stpl.html#embedded-python-code --Yuval

On Thu, Dec 15, 2011 at 10:11:01AM +0000, Jeremy Sanders wrote:
<code> <line indent="0">def foo():</line> <line indent="1">a = 0</line> <line indent="1">for i in range(10):</line> <line indent="2">a += i</line> <line indent="1">return a</line> </code> ;-) Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On Thu, Dec 15, 2011 at 5:31 AM, Jeremy Sanders <jeremy@jeremysanders.net> wrote:
This would be awkward when the code is supposed to produce an attribute value, so that entire elements are inside an element-open tag.
Nice. I'd like the option of JSON too, please, though the brackets might be a bit confusing :-)
If you're willing to wrap each line in a string, then leading whitespace will already be preserved. If you're not willing to wrap each line in a string, then it isn't clear how JSON would represent the code part, regardless of how indention is conveyed. -jJ

Need a new why of indentation? (V) (;,,;) (V) Why not Zoidberg?? Seriously though, I don't think something like this will be ever accepted. It totally goes against pythons idea behind indentation. On 12/12/2011 11:07 PM, Ron Adam wrote:

On 2011-12-11, at 01:16 , Mike Meyer wrote: pyc files directly? That would avoid the issue would it not? And if users are supposed to edit (therefore read) the generated code, wouldn't you want to make the file actually readable, therefore use indentation-based blocks?
The short version is: http://www.haskell.org/onlinereport/lexemes.html#lexemes-layout * when a brace is omitted after a `where`, `let`, `of` or `do` keyword, a brace is inserted and the indentation of the next lexeme is remembered * if the next lexeme is indented below the keyword, immediately insert a closing brace (before the lexeme itself) * otherwise, for each subsequent line - if it contains only whitespace or is indented further than the current level, continue the current expression - if it is indented at the current level, insert a semicolon - if it is indented below the current level, insert a closing brace You can find the complete and more formal definition at http://www.haskell.org/onlinereport/syntax-iso.html#layout

On 12/10/2011 12:06 AM, Nick Coghlan wrote:
I believe you mean return the value of the last statement that is executed, as in the try-except example or the conditional example: {: if a {:b} else {:c}} I must congratulate you, Nick, for being the first person in my 14 years with Python to make a brace idea look half-way sensible. I can see that algorithmically generated code (which includes templates) might be a real use case. -- Terry Jan Reedy

On Sun, Dec 11, 2011 at 10:46 AM, Terry Reedy <tjreedy@udel.edu> wrote:
Indeed, that was what I meant, albeit not what I wrote :)
I actually surprised myself - I started writing that purely as a devil's advocate "if you're going to rant about something to that extent, you may as well *try* to be constructive" post, but the end result was significantly less terrible than I originally expected :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (19)
-
Bruce Leban
-
Ethan Furman
-
Greg Ewing
-
Guido van Rossum
-
Jeremy Sanders
-
Jim Jewett
-
Masklinn
-
Massimo Di Pierro
-
Mathias Panzenböck
-
Matt Joiner
-
Mike Meyer
-
MRAB
-
Nick Coghlan
-
Oleg Broytman
-
Paul Moore
-
Ron Adam
-
Steven D'Aprano
-
Terry Reedy
-
Yuval Greenfield