Digressions from my discussion of python's future

I digress, badly. Since I want the main thrust of the discussion to read well, I know I have to remove the digressions. Being somewhat shy of actually deleting them, I move them aside. Here they loiter …

Note that one could arrange for the built-in types to have all their magic attributes by having the interpreter fake these up, when called for, using on-the-fly lambda expressions: they need not be carried around by the built-in objects themselves … though I believe this can be made the cheaper solution.

Rumour has it that Guido intends to change the behaviour of integer division so that it coerces to float (if needed ?), which sounds eminently sensible: and we'll still have divmod for the rounded form.

More games with *s

Keyword arguments are allowed to use the names of safe tunnels, provided the ** has an identifier: they appear, with their values, in the dictionary bound to this identifier. Safe tunnels need to be invisible, hence so do their names. This matches the present handling of the names of *-item and **-item:


>>> def demo(*args, **what): print what
... 
>>> demo(what=1, args=0)
{'args': 0, 'what': 1}

There is nothing to stop us allowing the ** to appear among, or even before, the purely positional parameters: this would forbid supplying the later purely positional parameters as keyword arguments, obliging callers to provide these inputs as positional arguments; indeed, placing ** before even one of the positional parameters would force all of these to be supplied by positional arguments. Equally, I have thought of no strong argument in favour of this liberty.

In a similar vein, it'd be entirely reasonable to allow parameters, after the single *, to be given without values, implicitly reading each such name as name=name, transcribing this name from the suite which defines the function into its execution initialisers.

Notes for the interpreter

Under the bonnet, a namespace object, obj, is a packaging of a python-callable, func, and getattr(obj, key), finding its first argument to be of the namespace-object type, calls the func packaged by obj, delivering func(key); so obj.name is func('name').

When running the suite of a builder statement in the name-space of a suite-wrapper, the interpreter will be catching AttributeError, if raised by attribute lookup or deletion on the suite-wrapper, and transforming it into NameError.


>>> class dummy: del nom
... 
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 1, in dummy
NameError: nom
>>> class dummy: nom
... 
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 1, in dummy
NameError: nom

I've also seen a proposal which provided for temporary namespaces, along the following lines (here using keywords using and do: I forget what the original used):


using:
    x = 7
    y = 32
do:
    z = x * y

in which x and y are temporary variables, visible only during the suites of using and do, z gets set in the enclosing namespace, but the temporary variables are read-accessible during the do clause which sets it. I can model this by using an anonymous namespace object borrowing from our original locals, providing no attribute modifiers of its own (so it'll borrow these from locals) but with a suite-wrapper which does carry attribute modifiers, which alter what the anonymous object's namespace dictionary (which the suite-wrapper alone can see as __dict__) contains. Use this suite-wrapper for the using clause's suite, use the anonymous object itself for the do clause; the former will store attributes on the anonymous object, the latter will read from it but store in the original locals. Discard the anonymous object and its suite wrapper when you are done.

Types

I haven't given much thought to the built-in type: I'm confident that all uses of it should be replaced with uses of isinstance, and I ultimately only believe in one type – function – albeit (necessarily) in two packagings: one as a python function, the other as a python lookup (i.e. namespace). All other types are illusions created by artful arrangments of objects and behaviour (and interfaces are what matters).

It will be necessary for objects (lookups) to support some form of do you support this interface questions, but these can (and should) be packaged using a magic method name: e.g. obj.__supports__ which takes some description of a python interface and returns true or false. This, however, doesn't interfere with what I'm doing, so I'll leave it to the hard-working souls who have devoted lots of time to thinking about describing interfaces.

While the basic infrastructure of python.py provides for objects whose namespaces are fixed once built, it may also (see the types-sig archive at python.org) be worth providing for modification of a namespace to be type-checked – that is, some infrastructure which; stores checker functions keyed on attribute name; when attribute modification is attempted, if the modified name has an associated checker, the value to be stored gets passed to the checker function, requiring (say) a false result for the modification to go ahead, reading true result as what the problem was. See also: check.py

Note that it is possible for such a checker to embrace deletion, signalled by calling the checker with no value to check. In general, one can use the withargs() hack to detect called with no argument, but more simply one can have the checker use a known default that it'll either reject or accept according as one wishes to allow or forbid deletion; thus obj.__getchecker__('mystringattribute') could return


def mustbestring(val=''):
    return type(val) is types.StringType
    # default of '' allows deletion of obj.mystringattribute in this case;
    # default of None would forbid it.

Ways out of the .__method__ problem

My suggestions deliberately err on the side of trying to stay as like python 1 as practical; but having a sub-namespace of a class to carry the method-functions to be inherited by instances is unavoidable. This leads to the one place where my suggestions will require a change to working code (as opposed to expanding the range of things that will work): where the methods of a class are looked up directly in the namespace of the class, as when a derived class over-rides a base's implementation of some method with a replacement which calls the method it's over-riding (e.g. __init__ and __del__). In such cases, the lookup will have to dereference via the name (here taken to be .__method__) of the sub-namespace which carries the class' methods; mybase.__init__(self) will become mybase.__method__.__init__(self).

Given that python 2 may involve changes dwarfing this, one alternative solution is to be forthright about this change: chose nice friendly names for the sub-spaces (drop the __ prefix and suffix, at least), dump the sophisticated initialisation wrapper of the new class and have normal class statements actually reflect the real structure of what gets built:


class new (old):
    instance method (method):
        def func(self, first, *args, **what, oldmeth=old.method.func):
            self.primal = first
            return apply(oldmeth, (self,) + args, what)

The instances of new will then inherit from new.meth, so invoke this method as self.func(…) as at present. This will force greater code-change, but might be a better approach: if nothing else, it'll let us put functions into the namespace of the class (on its data side) which aren't meant to be used as methods but simply as functions.

However, reverting to my conservative line, there remain some ways to take the edge off the problem. In the one-base case we can try something like


class new (old):
    def method(self, first, *args, **what, oldmethod=method):
        self.primal = first
        return apply(oldmethod, (self,) + args, what)

Note that we can read method, even though old.method wouldn't work (nor would new.method) when evaluating defaults for the new method's parameters – the namespace-object provided by the class builder does ensure that new's suite can read values in old.__method__'s namespace (because the suite's local namespace borrows from new.__method__ which borrows from the __method__ of each of new's bases). Likewise we could pass the old value of method through the namespace of the new class; but these tricks only work for the given method of one base. An __init__ method may well invoke those of its class's bases one by one; for this it is going to have to dereference through __method__.

Bodging __bases__ to divert the .__method__ problem

The __bases__ attribute could, at one extreme, be provided by the interpreter as a keyword argument to builders (though I'm not fond of the idea). In python.py I presume otherwise – if only so as to show how a builder can provide such names. This attribute could be set up by within, wrapped or instance: but I have left it to builder to do so (consequently, so does class), so that __bases__ is only an attribute of classes (and builder classes).

The __call__ used by gennie could record the bases of the .__method__ it sets up as the __bases__ to be seen by the suite. This would enable the suite (and, in particular, hidden tunnels into methods defined within the suite) to access methods supplied by base-classes by reading them directly as attributes of the corresponding entry in __bases__, rather than as attributes of the base-class' .__method__. For example:


        class new (recent, old):
            def __init__(self, first, *args, **what, 
                         oldinit=__bases__[1].__init__,
                         recinit=__bases__[0].__init__):
                recinit(self, first)
                return apply(oldinit, (self,) + args, what)

using __bases__[1] in place of old.__method__. There isn't enough mileage in this to persuade me to change things around for its sake, though.

Causal Boot-strap

Some tools would be much easier to build if only one had the tool in question to hand with which to build itself. This is called the boot-strap problem, in honour of the notion of pulling myself up by my boot-straps, a standard analogue of the classic which came first: the chicken or the egg ? paradox. One ends up building a useful tool some harder way than with itself.

One of my aims in these games is to have the fundamental meta-class construction tool behave exactly as if it had been created by itself. Although we actually have to build it some other way, I want there to be no way of knowing that it didn't build itself other than to apply the causal argument that it wasn't available for use until it had been created, so can't have been used to create itself.

At some stage, I must check how well my suggested implementation of builder measures up as a causal bootstrap.


Valid CSSValid HTML 4.01 Written by Eddy.