[Types-sig] Re: suggestion: replace class with a generic generator statement

Edward Welbourne Edward Welbourne <eddy@chaos.org.uk>
Thu, 6 May 1999 20:49:24 +0100 (BST)


Tim ended with the pertinent remark that if class weren't going to do
what it currently does, then my suggestion would be dead already.  One
of my primary aims was, indeed, to ensure that classes and their
instances (the things produced by calling them) behave like we're used
to.  I'm fairly sure I succeeded - though class becomes a builtin rather
than a reserved word and it's all *implemented* differently - so I must
infer that I've explained it poorly ...
This is no surprise.

Please *do not feel embarrassed* if you haven't understood me -
explaining my thoughts isn't my strong point ... but answering specific
questions I can usually manage: indicate which piece of text did the
confusing, ask a question on which you're stuck, explain what you *do*
understand (even if you're not sure) and I might spot what I've failed
to explain.  Eventually, I might even improve the pages to the point
where they make more sense ...

Furthermore, some clumsiness with my source-control tools at home meant
the web-site was missing various fragments until this evening
(crucially, class.py - see below).  Please, if you trip over any broken
links, tell me about them, even if you don't understand their context !

Victor (who has
> an intuition about the overall idea
so maybe there's hope for my writing after all ;^) said:
> ... meta^N-objects are supposed to do for meta^(N-1)-objects ...

There's an important point here - meta vanishes: meta^N = meta^(N-1) for
all N.  Indeed, the meta-class McLass defined in

    http://www.chaos.org.uk/~eddy/dev/toy/class.py

is a class and can't really tell that it wasn't created by instanciating
itself - except, of course, via the standard ontological proof ...
It's possible (given that I'm fairly sure of that code being `right')
that reading class.py will be more intelligible than reading my English.

Note that my calling the module class.py is deliberate `pushing the boat
out a little': the module provides a __call__ function and I really want
*any* object, ob, to be callable if ob.__call__ is callable.  However,
the class.__call__ function defined in class.py could equally have been
defined as __builtins__.class as far as anything important cares.  It
needs access to McLass, but that's all.  Indeed, McLass is the important
thing in all of this ...

Now, Victor also said:
> Edward, could you elaborate on your vision about the relationships
> between objects/classes/metaclass(es)/types and which of these
> entities exist according to you?

Right, so the first thing is that meta-classes vanish.  The existing
class notion will continue in being with (almost) its existing
semantics.  We can still use the name `instance' for the kinds of thing
produced by calling a class (i.e. by calling its .__call__() attribute).
However, I don't see

  class
  instance
  module
  package

being different types (in the existing sense): the underlying C or Java
will be treating *all* of these in the same terms.  I dream about
getting the other types (except probably actual functions, as generated
by the def statement) to be of this one type, but that's a dream until I
write the C toy (on which, for various reasons of distraction, I've done
nothing for months - sorry) and not necessary to the present scheme.

Each of these flavours of name-space/object will have an assortment of
callables in its __lookups__ attribute: the difference between classes
and modules will just be a difference in what callables the object uses
to do its attribute lookup.  The differences between classes and
instances will be down to the data in the concealed arguments of these
callables (so it'll be rather hard to detect).

Continuing to take Victor's message in reverse order,
> I'm unclear on what the class/object model looks like.
Well, yes, I didn't explain it very well.  I'll try a sketch ...

Refer to anything with a name-space as an `object', whether it's a class,
instance, module, package, ... or whatever.

An object's name-space is *entirely* consequent on and controlled by its
magic attribute, __lookups__, which is a list of callables.  The *only*
thing getattr does is (schematically):

def getattr(obj, key):
    for lkp in obj.__lookups__:
        try: return lkp(key)
        except AttributeError: pass
    raise AttributeError, key

except that (see python.py) the __lookups__ attribute itself is
accessible before getattr gets involved - the actual implementation of
getattr() is *simpler* even than this schematic.

Python has various magic names, __magic__, associated with supporting
the various interfaces defined in the language (e.g. magic in [ add,
mul, call, setattr, delattr, getitem, setslice ]).  These are carried
over verbatim into the new regime.  (But __dict__ ceases being magical,
being demoted to merely an implementation detail of some of the other
magic - setattr, delattr and one of the callables in the object's
__lookups__ list - retained for backwards compatibility).  The entire
semantics of *all* the flavours of object are defined in terms of these
magic attributes and the __lookups__.

So, e.g., if an entry in obj.__lookups__ returns an answer to input
'__setattr__', this had better be a callable, taking (key, val): when a
piece of code attempts to execute obj.name = value, this callable will
be invoked (and if no such callable is available, the code obj.name =
value will raise AttributeError('__setattr__')).  Invoking it needn't
change the result of calling getattr(obj, 'name'): albeit, in such a
case, we might be somewhat irritated by whoever implemented the object
in question.

Note that each lookup was just given a key (not, e.g., lkp(obj, key))
and the obj.__setattr__ function was called with args (key, value).
This has to be managed by using curried functions, but one can do those
in python (though I suspect my suggestion will make a builtin currie()
function necessary in practice): the value of obj.__setattr__ is
probable the same as

   lambda k, v, __o=obj: __o.__class__.__setattr__(__o, k, v)

but all of that is hidden inside the machinery of the builtin callable
class().

OK, that's a start: ask more questions, get more answers ...
Eventually I'll try to fill the quite proper request someone made to put
an outline into just one screen-full.

    Eddy.