defmacro (was: Anonymous blocks)
Guido:
My problem with macros is actually more practical: Python's compiler is too dumb. I am assuming that we want to be able to import macros from other modules, and I am assuming that macros are expanded by the compiler, not at run time; but the compiler doesn't follow imports ...
Expanding at run-time is less efficient, but it works at least as well semantically. If today's alternative is manual cut-n-paste, I would still rather have the computer do it for me, to avoid accidental forks. It could also be done (though not as cleanly) by making macros act as import hooks. import defmacro # Stop processing until defmacro is loaded. # All future lines will be preprocessed by the # hook collection ... from defmacro import foo # installs a foo hook, good for the rest of the file Michael Chermside:
I think it would be useful if we approached it like this: either what we want is the full power of macros (in which case the syntax we choose should be guided by that choice), or we want LESS than the full power of macros. If we want less, then HOW less?
In other words, rather than hearing what we'd like to be able to DO with blocks, I'd like to hear what we want to PROHIBIT DOING with blocks. I think this might be a fruitful way of thinking about the problem which might make it easier to evaluate syntax suggestions. And if the answer is that we want to prohibit nothing, then the right solution is macros.
I'm personally at a loss understanding your question here. Perhaps you could try answering it for yourself?
Why not just introduce macros? If the answer is "We should, it is just hard to code", then use a good syntax for macros. If the answer is "We don't want xx sss (S\<! 2k3 ] to ever be meaningful", then we need to figure out exactly what to prohibit. Lisp macros are (generally, excluding read macros) limited to taking and generating complete S-expressions. If that isn't enough to enforce readability, then limiting blocks to expressions (or even statements) probably isn't enough in python. Do we want to limit the changing part (the "anonymous block") to only a single suite? That does work well with the "yield" syntax, but it seems like an arbitrary restriction unless *all* we want are resource wrappers. Or do we really just want a way to say that a function should share its local namespace with it's caller or callee? In that case, maybe the answer is a "lexical" or "same_namespace" keyword. Or maybe just a recipe to make exec or eval do the right thing. def myresource(rcname, callback, *args): rc=open(rcname) same_namespace callback(*args) close(rc) def process(*args): ... ... if __name__ == '__main__': myresource("file1", process, arg1, arg2)
It could also be done (though not as cleanly) by making macros act as import hooks.
import defmacro # Stop processing until defmacro is loaded. # All future lines will be preprocessed by the # hook collection ... from defmacro import foo # installs a foo hook, good for the rest of the file
Brrr. What about imports that aren't at the top level (e.g. inside a function)?
Why not just introduce macros?
Because I've been using Python for 15 years without needing them? Sorry, but "why not add feature X" is exactly what we're trying to AVOID here. You've got to come up with some really good use cases before we add new features. "I want macros" just doesn't cut it.
If the answer is "We should, it is just hard to code", then use a good syntax for macros. If the answer is "We don't want
xx sss (S\<! 2k3 ]
to ever be meaningful", then we need to figure out exactly what to prohibit. Lisp macros are (generally, excluding read macros) limited to taking and generating complete S-expressions. If that isn't enough to enforce readability, then limiting blocks to expressions (or even statements) probably isn't enough in python.
I suspect you've derailed here. Or perhaps you should use a better example; I don't understand what the point is of using an example like "xx sss (S\<! 2k3 ]".
Do we want to limit the changing part (the "anonymous block") to only a single suite? That does work well with the "yield" syntax, but it seems like an arbitrary restriction unless *all* we want are resource wrappers.
Or loops, of course. Pehaps you've missed some context here? Nobody seems to be able to come up with other use cases, that's why "yield" is so attractive.
Or do we really just want a way to say that a function should share its local namespace with it's caller or callee? In that case, maybe the answer is a "lexical" or "same_namespace" keyword. Or maybe just a recipe to make exec or eval do the right thing.
def myresource(rcname, callback, *args): rc=open(rcname) same_namespace callback(*args) close(rc)
def process(*args): ...
But should the same_namespace modifier be part of the call site or part of the callee? You seem to be tossing examples around a little easily here. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On 4/25/05, Guido van Rossum <gvanrossum@gmail.com> wrote:
It could also be done (though not as cleanly) by making macros act as import hooks.
Brrr. What about imports that aren't at the top level (e.g. inside a function)?
Bad style already. :D If you want to use the macro, you have to ensure it was already imported. That said, I did say it wasn't as clean; think of it like pre-caching which dictionary that resolved an attribute lookup. Don't start with the complexity, but consider not making the optimization impossible.
Why not just introduce macros?
Because I've been using Python for 15 years without needing them?
And also without anonymous blocks or generator finalizers or resource managers.
Sorry, but "why not add feature X" is exactly what we're trying to AVOID here.
If anything is added, it might be better to add a single generalized tool instead of several special cases -- unless the tool is so general as to be hazardous. Unlimited macros are that hazardous.
If the answer is "We don't want
xx sss (S\<! 2k3 ]
to ever be meaningful", then we need to figure out exactly what to prohibit.
I don't understand what the point is of using an example like "xx sss (S\<! 2k3 ]".
The simplest way to implement macros is to add an import hook that can modify (replace) the string containing the source code. Unfortunately, that would allow rules like "replace any line starting with 'xx' with the number 7" Outside of obfuscators, almost no one would do something quite so painful as that ... but some people would start using regex substitutions or monkey-patching. I would hate to debug code that fails because a standard library module is secretly changed (on load, not on disk where I can grep for it) by another module, which doesn't even mention that library by name... As Michael said, we have to think about what transformations we do not want happening out of sight. I would have said "Just use it responsibly" if I hadn't considered pathological cases like that one.
[yield works great for a single "anonymous block", but not so great for several blocks per macro/template.]
Pehaps you've missed some context here? Nobody seems to be able to come up with other [than resource wrappers] use cases, that's why "yield" is so attractive.
Sorry; to me it seemed obvious that you would occasionally want to interleave the macro/template and the variable portion. Robert Brewer has since provided good examples at http://mail.python.org/pipermail/python-dev/2005-April/052923.html http://mail.python.org/pipermail/python-dev/2005-April/052924.html
Or do we really just want a way to say that a function should share its local namespace with it's caller or callee? In that case, maybe the answer is a "lexical" or "same_namespace" keyword. Or maybe just a recipe to make exec or eval do the right thing.
But should the same_namespace modifier be part of the call site or part of the callee?
IMHO, it should be part of the calling site, because it is the calling site that could be surprised to find its own locals modified. The callee presumably runs through a complete call before it has a chance to be surprised. I did leave the decision open because I'm not certain that mention-in-caller wouldn't end up contorting a common code style. (It effectively forces the macro to be in control, and the "meaningful" code to be callbacks.) -jJ
It seems that what you call macros is really an unlimited preprocessor. I'm even less interested in that topic than in macros, and I haven't seen anything here to change my mind. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
A couple of examples out of my tired head (solely from a user perspective) :-) Embedding domain specific language (ex.: state machine): stateful Person: state Calm(initial=True): def react(event): self.chill_pill.take() ignore(event) state Furious: def react(event): self.say("Macros are the evil :)") react(event) # xD p = Person() p.become(Furious) p.react(42) --- Embedding domain specific language (ex.: markup language): # no, i haven't thought about whether the presented syntax as such is unambiguous # enough to make sense def hello_world(): <html>: <head>: <title>: "Tralalalala" <body>: for g in uiods: <h1>: uido2str(g) --- Embedding domain-specific language (ex.: badly-designed database table): deftable Player: id: primary_key(integer) # does this feel backward? handle: string fans: m2n_assoc(Fan) --- Control constructs: forever: print "tralalala" unless you.are(LUCKY): print "awwww" I'm not sure whether this the Python you want it to become, so in a certain sense I feel kind of counterproductive now (sublanguage design is hard at 11 PM, which might actually prove someone's point that the language designer shouldn't allow people to do such things. I'm sure other people are more mature or at least less tired than me, though, so I beg to differ :-), Michael On 4/25/05, Guido van Rossum <gvanrossum@gmail.com> wrote:
It seems that what you call macros is really an unlimited preprocessor. I'm even less interested in that topic than in macros, and I haven't seen anything here to change my mind.
-- --Guido van Rossum (home page: http://www.python.org/~guido/) _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/michael.walter%40gmail.com
[ Michael Walter ]:
A couple of examples out of my tired head (solely from a user perspective) :-)
Embedding domain specific language (ex.: state machine): ...
Embedding domain specific language (ex.: markup language): ...
Embedding domain-specific language (ex.: badly-designed database table): ...
..., which might actually prove someone's point that the language designer shouldn't allow people to do such things.
The whole macros issue comes to a tradeoff between power+expressiviness X readability. IMVHO, macros are readability assassins. The power (for any developer) to introduce new syntax is *not* a desirable feature, but something to be avoided. And that alone should be a stronger argument than a hundred use cases. cheers, Senra -- Rodrigo Senra -- MSc Computer Engineer rodsenra(at)gpr.com.br GPr Sistemas Ltda http://www.gpr.com.br/ Personal Blog http://rodsenra.blogspot.com/
On 4/26/05, Rodrigo Dias Arruda Senra <rodsenra@gpr.com.br> wrote:
IMVHO, macros are readability assassins. The power (for any developer) to introduce new syntax is *not* a desirable feature, but something to be avoided. And that alone should be a stronger argument than a hundred use cases.
Personally, I believe that EDSLs can improve usability of a library. I've been following this list for quite a while, and trying to see what lengths (hacks) people go (use) to implement "sexy" syntax can give you quite an idea that custom syntax matters. And surely all of these tricks (hacks) are way harder to use than a EDSL would be. Regards, Michael
participants (4)
-
Guido van Rossum
-
Jim Jewett
-
Michael Walter
-
Rodrigo Dias Arruda Senra