
Hello Holger, On Tue, Jan 28, 2003 at 03:33:09PM +0100, holger krekel wrote:
Ok. But we must keep all doors open by expressing things abstractedly, like defining classes for C function description. By default, in the "no-op down-translation" obtained by running the Python-in-Python code over CPython, the actual calls are implemented with ctypes.
Maybe not even that for starters.
Everything that forces a particular down-translation is bad, even if that particular down-translation seems good.
I am not sure i understand what you mean here.
It seems we agree with each other. Sorry if I confused you. I was saying that the Python-in-Python interpreter itself should only rely on some custom descriptions for the external C functions. By "down-translation" I mean the same as your "next (generational) step", i.e. the statical analysis of the Python-in-Python source to produce lower-level code (e.g. C). Various down-translations will do various things from these C function descriptions.
IMO It's very comfortable to have a version which is verified to run on CPython (and Jython while we are at it) but can be used for the next (generational) steps.
Yes, I was pointing out that the role of ctypes is particular in (only) this respect: it will be probably be needed to run this verification --- unless all calls are also available from built-in modules provided by CPython.
What i am aiming at is something like the following set of restriction for implementing the pypy-python-interpreter:
I generally agree with you, although I would like to keep high-level Python structures available. I think the exact list will depend on what we feel to be necessary in a nice implementation, balanced against the expected complexity of the static analysis. In general I'd tend to favor a nice implementation.
- no nested scopes
We may even try to avoid nested functions altogether, and define more methods in our classes (unless it becomes confusing).
- simple function calls preferably with no arguments
Why not? Arguments are an essential abstraction which allow for much more optimizations than side-effect-based operations like storing values into instance attributes.
- no list comprehension
I've nothing against them. They are surely more conceptual than the corresponding "for" loop. We could reserve their use for particular cases, like when the expression to compute each item has no side-effects (so we would say "[x+2 for x in y]" but not "[f(x) for x in y]" if f() has side-effects). In other words we could use list comprehensions that would work even if the construction returned a generator instead of directly computing the list.
- no generators
Ok.
- no += *= and friends
I've nothing against them, but ok.
- global namespace contains only immutable objects
Yes.
- very explicit names: always do e.g. 'self.valuestack.pop()' instead of 'self.pop()'
Yup. Other restrictions I would suggest: - don't rely on simple operations throwing IndexError or OverflowError unless explicitely caught, e.g. all list item accesses should either be sure to fall within range, or be syntactically enclosed in a try:except: clause. - don't use the same variable, function argument or instance attribute to hold values of possibly multiple types. Use an explicit 'assert isinstance...' here and there. Eventually a straightforward global program analysis should be able to know which variable holds which type, for the majority of variables. - we can make exceptions to this rule, e.g. to allow either a value or None. In general I favor explicit special-cases: I much prefer a variable to contain None to mark a special value, than some value of the same type as the variable normally contains, like -1. Even better, when possible, throw an exception or perform some other altogether different action instead. A bientôt, Armin.