Re: [Python-ideas] Inline Function - idea
Alex, can you explain the difference (if any) between your proposal and dynamic scoping?
-- Steven
Inline functions would be a bit like dynamic scoping and a bit like macros. The main difference from dynamic scoping is that they would not search up beyond their parent to find names, since they act exactly like code injected at the spot they are called it is expected that the variables they are using (like the ones in it's parent) are either locals or globals. I'm not sure that that has many advantages outside of demanding less from the runtime, but that's how I imagined it. IMO The biggest advantage to inline functions over other constructs is ease of understanding. We may you be used to understanding scoping after lots of programming, but it's not always intuitive. On the other hand it is extremely intuitive to understand "when you call this, all that code runs exactly as if you had typed it here". - Alex
On Feb 18, 2014, at 9:07, Alex Rodrigues <lemiant@hotmail.com> wrote:
Alex, can you explain the difference (if any) between your proposal and dynamic scoping?
-- Steven
Inline functions would be a bit like dynamic scoping and a bit like macros. The main difference from dynamic scoping is that they would not search up beyond their parent to find names, since they act exactly like code injected at the spot they are called it is expected that the variables they are using (like the ones in it's parent) are either locals or globals.
I don't see how that's any different from a macro. If you're just proposing a limited form of macro that works entirely at the semantic or computational level (so it can't be used to add new syntax or do many of the other tricks that PyMacro, lisp macros, etc. do), that seems reasonable. But I don't think insisting that it's really a function, not a macro, makes it better.
I'm not sure that that has many advantages outside of demanding less from the runtime, but that's how I imagined it.
If I remember correctly, in your proposal you wanted to be able to create these things at runtime, and convert regular functions into inline functions on the fly. If so, I think you're demanding a lot _more_ from the runtime in the service of trying to demand less. Is that a key part of your proposal? If it's _not_ a key part of the idea, you really should play with using PyMacro (limiting yourself to pure "function-like" usage) and see if it gives you everything you need. If so, you'll have some solid examples of what this can do, why it works, how it's safer than a more general macro system like PyMacro, etc.
IMO The biggest advantage to inline functions over other constructs is ease of understanding. We may you be used to understanding scoping after lots of programming, but it's not always intuitive. On the other hand it is extremely intuitive to understand "when you call this, all that code runs exactly as if you had typed it here".
I'm not sure that it is. And it definitely gets in the way of reading code later on. That's exactly why C++ and C added inline functions, which do _not_ use the calling or defining scope, to replace function-like macros. Stroustrop even said that if you find it necessary to #define a macro, you've found a bug either in your code or in the language. I assume he later backed down from that assertion a bit, but the basic sentiment is shared by most of the community. It's also why later Lisp-family languages added hygienic macros either in place of or alongside nonhygienic macros. Capturing and modifying variables from a different scope is often useful, but it's even more often a mistake. That's why Python, Ruby, C++, and many other languages require you to make it explicit (all in different ways) even for simple closures over directly visible variables.
I'm not sure that that has many advantages outside of demanding less from the runtime
MacroPy doesn't demand anything from the runtime at all. *pip install macropy *and you're off to the races. I'm not sure how you can get any less demanding than that. If you really want inline functions as you described, just write a macro using an unhygienic quasiquote: *@macros.expr()* *def func(tree):* * return q[log(ast[tree], "i am a cow")]* This will inline the code *log(..., "i am a cow") *into anywhere you call *func(...)*. What you're asking for is already here, not tomorrow but today: you don't need to persuade anyone that it increases ease of understanding, or that it's a good idea, or write a PEP. If you think it'll be worth someone else's effort to fork the python interpreter, trying out the idea using MacroPy isn't a lot to do in advance. On Tue, Feb 18, 2014 at 10:09 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Feb 18, 2014, at 9:07, Alex Rodrigues <lemiant@hotmail.com> wrote:
Alex, can you explain the difference (if any) between your proposal and dynamic scoping?
-- Steven
Inline functions would be a bit like dynamic scoping and a bit like macros. The main difference from dynamic scoping is that they would not search up beyond their parent to find names, since they act exactly like code injected at the spot they are called it is expected that the variables they are using (like the ones in it's parent) are either locals or globals.
I don't see how that's any different from a macro.
If you're just proposing a limited form of macro that works entirely at the semantic or computational level (so it can't be used to add new syntax or do many of the other tricks that PyMacro, lisp macros, etc. do), that seems reasonable. But I don't think insisting that it's really a function, not a macro, makes it better.
I'm not sure that that has many advantages outside of demanding less from the runtime, but that's how I imagined it.
If I remember correctly, in your proposal you wanted to be able to create these things at runtime, and convert regular functions into inline functions on the fly. If so, I think you're demanding a lot _more_ from the runtime in the service of trying to demand less. Is that a key part of your proposal?
If it's _not_ a key part of the idea, you really should play with using PyMacro (limiting yourself to pure "function-like" usage) and see if it gives you everything you need. If so, you'll have some solid examples of what this can do, why it works, how it's safer than a more general macro system like PyMacro, etc.
IMO The biggest advantage to inline functions over other constructs is ease of understanding. We may you be used to understanding scoping after lots of programming, but it's not always intuitive. On the other hand it is extremely intuitive to understand "when you call this, all that code runs exactly as if you had typed it here".
I'm not sure that it is. And it definitely gets in the way of reading code later on. That's exactly why C++ and C added inline functions, which do _not_ use the calling or defining scope, to replace function-like macros. Stroustrop even said that if you find it necessary to #define a macro, you've found a bug either in your code or in the language. I assume he later backed down from that assertion a bit, but the basic sentiment is shared by most of the community.
It's also why later Lisp-family languages added hygienic macros either in place of or alongside nonhygienic macros.
Capturing and modifying variables from a different scope is often useful, but it's even more often a mistake. That's why Python, Ruby, C++, and many other languages require you to make it explicit (all in different ways) even for simple closures over directly visible variables. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Feb 18, 2014 at 10:07:20AM -0700, Alex Rodrigues wrote:
Alex, can you explain the difference (if any) between your proposal and dynamic scoping?
-- Steven
Inline functions would be a bit like dynamic scoping and a bit like macros. The main difference from dynamic scoping is that they would not search up beyond their parent to find names,
What is "their parent"? Is that the function they are defined in, or the function they are called in?
since they act exactly like code injected at the spot they are called it is expected that the variables they are using (like the ones in it's parent) are either locals or globals.
But that is *not how it works* if you actually type the code in that spot. If you have code that looks like this: def spam(): def eggs(): def cheese(): ### <= insert code here then code inserted into cheese *will* search the eggs and spam local variables as well as globals. Having "inline code" skip over eggs and spam namespaces is a major difference in behaviour. The advantage of dynamic scoping is that it is a standard, well-understood execution model that can easily be described. The pros and cons are well known. There are more cons than pros, which is why very few modern languages support it. Your suggestion seems to be half dynamic, half lexical (static), but not quite the same as either. So it would be completely innovative, and since nobody has used this before, nobody can know how well it will work in practice.
I'm not sure that that has many advantages outside of demanding less from the runtime,
How do you know it demands less from the runtime?
but that's how I imagined it. IMO The biggest advantage to inline functions over other constructs is ease of understanding.
Well I don't understand it. Flat scoping, like in Python before nested_scopes was introduced, that's easy. Nested scopes (static or lexical scoping), that's also easy to understand: scope follows the layout of your code when indented. That's extremely intuitive. Dynamic scoping is a bit harder to grasp, but not very much so. A function sees the variables in the code that calls it, not the code where it was defined. So if you have a function spam that calls eggs that calls cheese, cheese sees the local variables inside eggs and spam. But yours, well, I'm not quite sure how it's supposed to work, beause it is half static and half dynamic and all confusing. I believe that you think this is a simple model to understand because you're only thinking of simple cases. But what happens if you have an inline function call an inline function? def regular(): x = 42 inline() @make_inline def inline(): another_line() @make_inline def another_inline(): print(x) According to your comment above, another_inline should NOT follow the normal dynamic scoping search path: - does another_inline have a local variable x? - if not, does inline have a local variable x? - if not, does regular have a local variable x? <=== FOUND - if not, look for a global instead it should follow: - does another_inline have a local variable x? - if not, does inline have a local variable x? - do not search regular, since that is too deep - skip straight to globals But that contradicts the idea that the code in another_inline should behave just as if it were typed inside inline. So your description is ambiguous -- will calling regular() above raise a NameError, or will it print 42? There are *all sorts* of odd corners to be considered. What happens if another_inline is declared *inside* inline, rather than outside? What happens if another_inline is declared inside *regular*? What if it's a closure that has come from somewhere else? I don't have any intuition to be able to immediately guess what the behaviour in these cases will be. I think that either static or dynamic scoping is easy to reason about, but your hybrid is not.
We may you be used to understanding scoping after lots of programming, but it's not always intuitive. On the other hand it is extremely intuitive to understand "when you call this, all that code runs exactly as if you had typed it here".
But that's not what you described earlier. See my earlier comment. -- Steven
participants (4)
-
Alex Rodrigues -
Andrew Barnert -
Haoyi Li -
Steven D'Aprano