On Fri, Apr 08, 2022 at 08:24:40AM +0000, Malthe wrote:
But firstly, let me present the idea. It is very simple, that Python should have declarative imports,
I'm not sure I understand why you consider this "declarative". https://en.wikipedia.org/wiki/Declarative_programming As I see it, this proposal merely adds a unary operator for implicit imports.
For example, `some_regex = @re.compile(...)`.
What happens then is that before anything else in that module, that symbol is imported:
from re import compile as _mangled_re_compile
It must be the very first thing (hoisting) because when else would it happen?
On-demand. As you say yourself, this could be a dynamic import at the time of use.
It's been suggested before to have a shorthand syntax which does a dynamic import at the time of using it but this brings me to the twist:
We want typing to pick up these imports.
At the moment static type checkers have to look for two import statements: `import spam`, and `from spam import eggs`. With this proposal, they will still need to look for those two import statements, but they will also need to parse every expression looking for `@` as a unary operator: result = 2*x**3 - 3*y + @math.sin(x*y) - 5*x*y**2 This does not help typing at all. It just means the type-checker has to do more work to recognise imports.
And this twist has a second leg which is that we often need to import symbols simply in order to type some argument type or return type. This leads to a great many more imports to type.
Code is read much more than it is written. I would much prefer to have explicit imports (by convention, if not necessity) in one place at the top of the page, than to mystery symbols where the implicit import could be buried far away. Right now, this is an error: # Start of module. obj = wibble.foo because wibble is not a built-in nor a global (excluding weird shenanigans committed by other modules), so the name "wibble" doesn't exist. But with hoisting, that import could be *anywhere* in the file. Even in dead code. # Start of module. obj = wibble.foo ... ... # five pages of code later for a in obj.method(): while flag: if condition: @wibble That's right, with hoisting you can use a module before you import it. Mind. Blown. Have pity on beginners, casual Python programmers, and the people who have to help them. Don't implement this horror. If it were merely on-demand imports, then we would know that the import @wibble would have to appear in actual, executed code before you can use wibble. But with hoisting, we don't even have that promise. It is truly spooky action at a distance. And to save nothing but an import line.
A situation where this would come in really handy is in scripting such as how we use Python in Apache Airflow to let users write out simple workflows. A workflow definition which could be a 5-liner quickly becomes a 20-liner – consider for example:
default_args = { "start_date": @datetime.datetime(...) }
That's just a three-liner, which becomes a four-liner: import datetime default_args = { "start_date": datetime.datetime(...) } -- Steve