What's better about Ruby than Python?

Alex Martelli aleax at aleax.it
Wed Aug 20 18:37:07 EDT 2003


Alexander Schmolck wrote:
   ...
> Is your assumption of my ignorance regarding the difference between
> mutation/rebinding maybe based on the implicit assumption that a class
> statement by some sort of logical necessity *has* to create a new class
> object and then bind a name to it?

You could put it that way (and my compliments for beating even me in
terms of intricate but correct sentences!) though it's an issue of
*good, simple design* rather than "logical necessity".  There is no
"logical necessity" that a class statement has anything to do with
classes: you COULD, logically, have it perform a print if the classname
has odd length and create classes only when the classname has even
length, taking alternate characters of the name in this case.  It
would be absurdly bad design, but "logical necessity" just can't apply.


> Is there a reason why a class X(...):... statement can't just mutate a
> preexisting, former version of the class X (provided there is one?). Maybe

Of course: context-dependence -- a very bad thing.

> there is, in which case let me state that I'm not particularly hung up on
> this, what I'm hung up about is the ability to easily update the behavior
> of pre-existing instances.

But you don't accept that having the class statement do it is horrible
language design -- complicating the semantics of 'class' in a way that
doesn't reflect any other statement in Python.  THAT is the crucial
point to me.  The crucial point to you is that there MUST be ways to
achieve this objective, and you don't care how badly-designed the
language might become in consequence; the crucial point to ME is that
the language remains as simple and well-designed as it is, as I see
"updating the behavior of pre-existing instances" as quite a minor issue
(and thus I think that the present semantics are quite acceptable for
the purpose, as I showed).  By assuming that your ideas weren't quite
clear on the rebinding/mutating distinction, I was paying you the
implici compliment (from my POV) of assuming you DID care for good
language design, and just couldn't see (due to incomplete grasp of
that distinction) why your desired/proposed language change would be
such an utter, unmitigated disaster; if you assure me you have full
grasp of that, then I must conclude that you don't care about such
horrible language degradation if it just lets you get your interactivity
du jour -- much the way, e.g., VB6 got to where it was, in all senses.
<shudder>.

So, let's try to show other readers WHY the intricate semantics you'd
like the class statement to have would be quite SO bad.  The "provided
there is one" parenthesis is the clue.
In your proposal, the class statement must first of all find out if
(in the same namespace where it would NORMALLY bind the name) that
name is already bound.  Then, if it's bound, it must examine whether
it's bound to A CLASS.  What does THAT mean, exactly?
    X = int
    class X: ...
is int 'a class'?  If not, why not?  If so, then what should this
'class X' statement DO -- fail with an exception?
And of course, many names are typically bound to the same object.
What semantics would you want for:
    class X: ...
    Y = X
    class Y: ...
should this update class X?  Name Y _IS_ after all bound to it in
that instant.  What if the name Y is in local scope but X isn't --
covert cross-scope mutation?  And what about:
    class X: ...
    x = X()
    X = 23
    class X: ...
at the time of the SECOND 'class X' statement, name X is NOT
bound to a class -- but it USED to be, and instances of that class
name X USED TO be bound to still do exist (and so does therefore
that class object, though not bound to that name any more) --
what semantics would be simple, regular and unsurprising for
THIS tweak...?

Oh, I could go on for hours, but I hope it's obvious enough that
the simple, crystal-like clarity of Python's current semantics,
where
    class X: ...
and
    X = <metaclass>('X', (), <classdict>)
are ALWAYS, RIGOROUSLY equivalent, is just too previous to give
up without a fight.  Drastic changes in a statement's semantics
depending on what names happen to be bound to what and in what
scope is just too horrible a language change to contemplate seriously.


> I'm happy to write some elisp and python code to do some extra work on top
> of py-execute-region on a key-press. By "adequately reflected" I mean
> amongst other things that I can easily change the behavior of existing
> instances of a class after I made changes to the source code of that
> class.

Can't help with elisp, sorry -- no idea on how IT would find out what
names are bound to what, where, in order to generate different code
in different instances.  I guess a Python IDE might easily do it for some
suitably restricted subset (e.g., global names in __main__ only, say).

>> oldX.__dict__.clear()
   ,,,
> I tried this some time ago and discovered it only worked for old style
> classes; the dict-proxies of new-style classes are, I think, not directly
> mutable, I'd guess for efficiency reasons.

True!  New-style classes' __dict__ is readonly-ish, and __bases__ not
so easy to tweak either.  You must do the update quite a bit more
carefully then, e.g.

>>> for m in dir(oldX):
...   if m.startswith('__') and m.endswith('__'): continue
...   else: delattr(oldX, m)
...
>>> for m in dir(newX):
...   setattr(oldX, m, newX.__dict__[m])

and the like (you must use newX.__dict__[m] to get at the functions and
descriptors, not getattr(newX,m), of course).  New-style classes DO
make metaprogramming a bit more delicate (in exchange for many other
advantages, of course) -- nowhere like IMPOSSIBLE, of course, just not
quite as immediate as old-style classes had let us get used to:-).

> I *think* it ought to be possible to effect all the necessary changes by
> directly manipulating attributes, rather than going via .__dict__, even
> for class/staticmethods.

Right, see above.

> It's too late now, so I haven't fully thought this through yet but I'll
> give it a try tomorrow.
> 
> Anyway, I still don't see a compelling reason why class statements
> couldn't/shouldn't be mutating, rather than rebinding. Is there one?

I think I've given several -- need I bring many more?


>> > AFAIK doing this in a general and painfree fashion is pretty much
>> > impossible in python (you have to first track down all instances -- not
>> > an
>> 
>> I personally wouldn't dream of doing this kind of programming in
>> production level code (and I'd have a serious talk with any colleague
>> doing it -- or changing __class__ in most cases, etc).
> 
> I'd be interested to hear your reasons. (The only argument against this

They boil down to: do the simplest thing that can possibly work.  Black
magic is seductive but it seduces you AWAY from real simplicity.


> 1. Redefining methods of class during execution, as part of the solution
> to
>    the problem the program is supposed to adress.
> 
> 2. Redefining classes as part of an incremental development process. In
> this
>    case, you of course "redo" the whole class statement, because you edit
>    the source code with the class statement and everything else in it and
>    then the most convinient way to update your code is to send all or part
>    of this newly edited file to your interactive session (plus potentially
>    some tweaking code that mutates the pre-existing "live" version of X as
>    you outlined above, or somehow else effects the changes you want to
>    take place in existing instances).
> 
> I'm chiefly interested in 2. (to the extent that I consider it an
> essential ability of a good programming language) and much less so in 1.
> (although modifying instances and classes can also sometimes be useful).

Sure can (which is why most of us forego the _other_ advantages of
functional and single-assignment languages).  But if you want my
opinion what's really missing to make Python a really interactive-
development language is the ability to easily save and restore the
whole state of a session -- a "workspace".  True "interactive
development" languages DO that (perhaps indeed they may encourage
reliance of this to excess).  THAT is a metaprogramming problem that
I personally find well beyond my own abilities.


>> Rarely does one want to modify an existing class object any more 'deeply'
>> than by just redoing a method or two, which this utterly simple
>> assignment entirely solves -- therefore, no real problem.
>> 
>> If your style of interactive programming is so radically different from
>> that of most other Pythonistas, though, no problem
> 
> (I wonder whether it is (quite possibly) and if so why? Developing code
> interactively just seems like the obvious and right thing to do to me.)

When I aim at producing a set of source files that, when run,
produce a certain effect, I find it hard to do that otherwise than
by running the set of source files afresh each time -- I use
interactive modes for exploration, but then transfer the results
of my exploration into self-sufficient sources (and I did that in
e.g. APL too, a "truly interactive" language at all costs -- and I
remember that colleagues who relied TOO much on the 'workspace'
features inevitably had problems coalescing together the right
set of source files, when exploration being done one wanted a
complete stand-alone library or program).


>> put some effort in understanding how things work (particularly the
>> difference between *MODIFYING AN OBJECT* and *REBINDING A NAME*), Python
>> supports you with power and simplicity in MOST (*NOT* all)
>> metaprogramming endeavours.
>> 
>> If you DO want total untrammeled interactive and dynamic metaprogramming
>> power, though, *THEN* that's an area in which Ruby might support you
>> even better
> 
> No it doesn't, for example Ruby doesn't have docstrings which is an
> unnecessary waste of my time (apart from the fact that Ruby offends my
> sense of esthetics).

I won't debate the aesthetics of the issue, I just think it's a bit of
a frail reed to make decisions on.


>> -- for example, this Python approach would *NOT* work if
>> oldClass was for example str (the built-in string class -- you CANNOT
>> modify ITS behavior).
> 
>> Personally, for application programming, I much prefer clear and
>> well-defined boundaries about what can and what just CANNOT change. But
>> if you want EVERYTHING to be subject to change, then perhaps Ruby is
>> exactly what you want.
> 
> Nope, in that case I'd almost certainly use smalltalk which I consider
> infinitely superior to ruby (or possibly some Lisp).

If you like smalltalk and/or Lisp's syntaxes, there are indeed few
reasons not to turn in that direction.  Ruby (like Python or Dylan)
attempt to give "more normal" syntax to similarly flexible and
powerful (by some definition) semantics.


>> Good luck with keeping track of what names are just names (and can be
>> freely re-bound to different objects) and which ones aren't (and are more
>> solidly attacked to the underlying objects than one would expect...).
> 
> I don't think we really fundamentally disagree. I don't think one should
> screw around with everything just because one can and I also think that
> languages who set inappropriate incentives in that direction are less
> suitable for production software development.

Yes, we do agree on this important issue.  But the idea of 'class X:'
semantics depending on what name X happened to be bound to at that point
in time and in what scope[s] is so abhorrent to me (while apparently
still your preference) that clearly there is some equally deep
disagreement.  The "keeping track of names" problem appear so totally
unsurmountable to me...!


Alex





More information about the Python-list mailing list