Dan Sugalski wrote:
At 7:55 AM -0400 10/18/04, Sam Ruby wrote:

I've been trying to make sense of Python's scoping in the context of Parrot, and posted a few thoughts on my weblog:

http://www.intertwingly.net/blog/2004/10/18/Python-Parrot-and-Lexical-Scopes

While I posted it on my weblog for formatting and linking reasons, feel free to respond on the mailing list. Suggestions welcome, in particular, a PIR equivalent to the Perl would be most helpful.

I responded (sorta) on the weblog, but I'll redo it here since it gets into some of the fundamental bits of namespaces, which checking the calendar I see we're scheduled to grovel over again.


The code (for folks playing along at home) is:

scope1.py:

 from scope2 import *
 print f(), foo
 foo = 1
 print f(), foo

and scope2.py:

 foo = 2
 def f(): return foo


I'll make two assumptions in the explanation here.

First, that lexical scopes are inappropriate. In this case, they'd do exactly what you want them, but the problem there is that you can't really have multiple files sharing the same scope, so that makes splitting modules into multiple files untenable.

In Python file == module.

But in any case, my thoughts were to arrange for Python it so that at depth zero in the scratchpad stack is always the python __builtins__. At depth one in the scratchpad stack is always the module namespace. Starting at depth two is the true lexical variables.

Globals would be a good place to store the set of loaded modules for quick access.

Second, that named namespaces are inappropriate. Again, in this case they could do what you wanted if scope1.py and scope2.py were in different basic namespaces. Modules split across multiple files could, with named namespaces (that is, things in the main module have their variables in main:, the foo module in foo:, and so forth), work just fine, but that's not what we need here, since python wants un-qualified names to look up, at runtime, in the current module namespace and then the main namespace.

Agreed.

So, the solution here is to have a chain of overlapping namespaces. Each sub or method has a handle on a namespace, which itself has a link to the namespace it's occluding, and so on up to the top, basic namespace. (If, indeed, we even have one that's universal -- code could twiddle with that if it really wanted to) The namespaces act much like the lexical pads do (or would, if they were fully functional) only with globally visible names instead.

The nice thing here is that this is transparent to the code -- the find_global and store_global ops may have to jump through some hoops to do the right thing, but most bytecode won't know it's happening.

I just want to make sure that you have any particular semantics in mind when you say "global" here. In Python, global essentially means module. Locals tend to obsure globals, so essentially what is desired is a find_local which does the right thing.


Consider the following code:

  def f(x): return len(x)
  for i in [0,1]:
    print f("foo")
    f = lambda x: x.upper()

The desired result is that the global len function is called once, and the global upper function is called once - via the local lambda function.

For this to work, f(x) needs to be unaware of any local/global distinction. The find_whatever opcode it uses needs to start with the locals and follow links until a match is found.

What we need to do is define and add the ops to add in and remove layers of namespaces, and get the packfile format set so that the proper layers can be anchored to the sub PMCs when bytecode's loaded in from wherever.

--
                Dan

- Sam Ruby

Reply via email to