On Apr 27, 2:40 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Fri, 27 Apr 2012 10:33:34 -0700, Adam Skutt wrote:
> >> Why should the caller care whether they are dealing with a singleton
> >> object or an unspecified number of Borg objects all sharing state? A
> >> clever interpreter could make many Borg instances appear to be a
> >> singleton. A really clever one could also make a singleton appear to be
> >> many Borg instances.
>
> > Trivial: to break cyclical references in a deep copy operation.
>
> I asked why the *caller* should care. If the caller has to break cyclical
> references manually, the garbage collector is not doing its job.

It's a necessary requirement to serialize any cyclical structure.
Garbage collection has nothing to do with it.  If I have some
structure such that A --> B --> A, I need to be able to determine that
I've seen 'A' before in order to serialize the structure to disk, or I
will never write it out successfully.

There are plenty of situations where we legitimately care whether two
pointers are the same and don't give one whit about the state of
objects they point to.  You cannot conflate the two tests, and that's
precisely what your 'give all borg instances the same identity' idea
does.

> I think that if you're talking about per-instance members of a Borg
> class, you're confused as to what Borg means.

I'm not.  I'm talking about per-instance members of a subclass of a
Borg class.  There's nothing about the Borg pattern that forbids such
behavior, which is one of the reasons it's such a terrible idea in
general.  Borg implies promises that it cannot readily keep.

> Since all instances share state, you can't have *per-instance* data.

I most certainly can do so in a subclass.  Shared state in a parent
doesn't mandate shared state in a child.

> Not at all. Obviously each Borg subclass will have it's own fake
> identity.
> When I said that Borg instances are indistinguishable except for
> identity, I thought that was obvious that I was talking about instances
> of a single type. Mea culpa.
>
> Clearly if x is an instance of Borg, and y is an instance of
> BorgSubclass, you can distinguish them by looking at the type. The point
> is that you shouldn't be able to distinguish instances of a single type.

No, that's not the least bit obvious nor apparent, and it still
violates LSP.  It means every function that takes a Borg as an
argument must know about every subclass in order to distinguish
between them.

The serialization function above would need to do so.  Imagine an
object x that holds a Borg object and a BorgSubclass object.  If the
serialization function keeps a list of objects it has seen before and
uses that to determine whether to write the object out, it will fail
to write out one or the other if we implemented your harebrained 'All
Borg objects have the same identity' idea.

Your idea means that 'x.borg is x.subborg' must return True.  It also
means either x.borg isn't going to be written out, or x.subborg isn't
going to be written out.  The program is broken.

If you modify your idea to ignore subtypes, than this function breaks:
def write_many(value, channel1, channel2):
    channel1.write(value)
    if channel2 is not channel1:
        channel2.write(value)

Calling write_many("foo", x.borg, x.subborg) now gives different
behavior than write_many("foo", x.borg, x.borg).  That's probably not
what the programmer intended!

Like it or not, whether you have only one object with shared state or
infinite objects with the same shared state is not an implementation
detail.  Just because you write code that doesn't care about that fact
does not make it an implementation detail.  I can write code that
depends on that fact, and there's not a single thing you can do to
stop me.

This is why the Borg pattern is a bad idea in general, because it
encourages programmers to write code that is subtly wrong.  If you
have a Borg class, you can't ignore the fact that you have multiple
objects even if you want to do so.  You will eventually end up writing
incorrect code as a result.  Yet, many people try to do precisely
that, your idea is attempting to do precisely that!

> Oh please, enough of the religion of LSP.
>
> Barbara Liskov first introduced this idea in 1987, twenty years after
> Simula 67 first appeared and thirty years after MIT researchers came up
> with the concept of object oriented programming. That's hardly
> fundamental to the concept of OOP.

> People have, and still do, violate LSP all the time.

People write code with security flaws all of the time too.  This
doesn't even being to approach being an reasonable argument.  It's
completely disingenuous.  People come up with ideas and fail to
properly formalize them all of the time.  People come up with useful,
revolutionary ideas and get parts of them wrong all of the time.

If you violate LSP, then you enable interface users to write buggy
code.  Correct class hierarchies must follow it; correct interface
implementations must follow it.  There's nothing optional about it, it
even applies even if you don't have objects at all.  It's just extra
inescapable for the sort of class hierarchies most OOP languages use.

> Besides:
>
> - In real life, subtypes often violate LSP. An electric car is a type of
> car, but it has no petrol tank. Wolf spiders have eyes, except for the
> KauaŹ»i cave wolf spider, which is is a subtype of wolf spider but is
> completely eyeless.

Yes, real life is more complicated than the simplistic relationships
that most class hierarchies support.  So what?  Where did I advocate
encoding such relationships using class hierarchies?

Adam
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to