What's better about Ruby than Python?

Alex Martelli aleax at aleax.it
Wed Aug 20 16:38:14 EDT 2003


Alexander Schmolck wrote:

> mertz at gnosis.cx (David Mertz) writes:
> 
>> Alexander Schmolck <a.schmolck at gmx.net> wrote previously:
>> |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 don't think there is any reason why the statement 'class' COULD NOT do
>> what you describe.  But doing so seems much less Pythonic to me.
>> 
>> In Python, there are a bunch of statements or patterns that ALWAYS and
>> ONLY binds names.  Having something that sometimes binds names, and
>> other times mutates objects... and you cannot tell the difference
>> without finding something that may be far away in the code.... well,
>> that's not how Pythonistas like to think about code.
> 
> I agree this argument has merrit but I'm not sure I find it completely
> compelling.

The counter-arguments you present in the following do not affect Mertz's
argument in the least, and thus cannot indicate why you don't find it
completely compelling.  To rephrase David's argument: simplicity suggests
that a 'class' statement should always have the same fundamental semantics.
Since it SOMETIMES needs to bind a name, therefore, letting it ALWAYS
bind a name -- rather than sometimes yes, sometimes no, depending on
context -- is the only Pythonic approach.  In other words,

class X: ...

ALWAYS means the same as:

X = <some metaclass>('X', (), <some dict>)

(for appropriate values of <some metaclass> and <some dict>) -- no ifs,
no buts.  Can't be simpler.  In particular, it's a LOT simpler than the
alternative semantics of

if <name X already defined in this scope>:
    ...something...
else:
    ...something totally different...

and I don't see you addressing this claim of greater simplicity in the
rest of your post.  So, since you say you DON'T agree with this, could
you perhaps elaborate?


Now, regarding the rest of your post:

> What's worse, looking at the top of a file and thinking "Ah so that's what
> class Foo does" and
> 
> a) overlooking that somewhere below a 2nd class Foo gets created (and one
>    assumes both with instances that insidously claim to be 'Foos').
> b) overlooking that somewhere below a 2nd class Foo gets mutated (and thus
> all
>    prexisting instances of it)
> 
> ?
> 
> I think we'd agree that both options are generally undesirable (so one

It's definitely undesirable to totally overlook either a rebinding or
a name, OR a mutation of a mutable object -- it makes no difference if
you're overlooking these crucial things for a class, a function, or what.
So, if you believe such distractions are frequent:

> could conceivably even make class statments with the same name in module
> scope raise an exception and have the user jump through some hoops if he
> *really* wants something along those lines, like having to define Foo #2
> in a function and have it returned).

...you're basically making a case for functional programming (perhaps of
the "single-assignment" variety): no object ever mutates, and each name
is bound ONCE only -- no rebinding.  Undoubtedly such languages do in
fact eliminate the mistake of somebody thinking that...:

    x = [1, 2, 3]
    ...much code overlooked...
    if len(x) > 3:
        <code the guy thinks will never execute>

in that either a rebinding of name x, such as
    x = [6, 7, 4, 3]
or a mutation of the object, such as
    x.append(23)
could in fact cause the code guarded by that 'if' to execute.

On the other hand, many people believe that functional programming is
hard -- that mutating data and rebinding names leads to a much easier
and free-flowing "style".  Python (like most mainstream languages)
definitely accepts the "many people"'s opinion, and does allow mutation
of mutable objects (such as lists, clases, &c) and rebinding of names.
So does Ruby, etc, etc.  It WOULD seriously and deleteriously impact
the concept of *FIRST-CLASSNESS OF ALL OBJECTS*, of course, if it was
any harder to rebind a name that happens to be bound to a class object
at some point, that one which happens instead to be bound to a list,
a function, or what-have-you; *THAT* wishy-washy choice would be a
terrible language design, while both Python's current design AND that
of (e.g.) Erlang are quite self-consistent even though opposites.

But this has nothing to do with any presumed desirability of statements
that do quite different things depending on what names happen to
be bound at the time said statements execute, of course.


> But at least for b) I can think of one use-case I find rather compelling,
> namely interactive development. I'd also think that the nasty surprise in
> a) might well be worse than in b), because at least all instances that
> claim to be Foos will behave the same.

Not really, unless you want to remove the limitation about "same scope"
too -- and get into a REAL mess.  Each Foo can STILL refer to a
totally different object, just in different scopes, e.g.:

def aFoo(x):
    if x%2:
        class Foo:
            def __str__(self): return 'odd!'
    else:
        class Foo:
            def __str__(self): return 'even!'
    return Foo()

someFoos = [aFoo(x) for x in range(8)]
for foo in someFoos:
    print foo.__class__, foo

There -- how would you complicate the semantics of the class statement
in order to ensure the property you assert holds, namely "all instances
that claim to be Foos will behave the same"?!  No, unless you redesign
and complicate the whole language to VASTLY deeper extent there is just
no way to ensure THAT.  And as you go on to admit, that "one use-case"
is anything BUT "compelling" anyway -- modifying existing class objects
is quite sufficient to support an interactive environment that works
the way you'd like, if anybody's interested in doing so.


Alex





More information about the Python-list mailing list